﻿#include "HeatMapWindow.h"
#include "TwoDHeatMapSettings.h"
#include "Curve2DPlotor.h"
#include "TextFileParser.h"
#include "DataHeatGraph2DSettings.h"
#include "DialogHeatGraph2DSettings.h"
#include "LineChartTracer.h"
#include "DialogTracer2DSettings.h"
#include "AnimationSettingData.h"
#include "CommonFunction.h"
#include <QDomElement>
#include <QDomDocument>
#include <QDomAttr>
#include <array>
#include <QTimer>
#include <QColor>
#include <QList>
#include <QtGlobal>
#include <QItemSelectionModel>
#include <vtkLookupTable.h>
#include <QListWidgetItem>
#include "BaseModule/IBaseSignal.h"
#include "BaseModule/GlobalHelper.h"
#include "vtkTable.h"
#include "AnimationTooBarInfoData.h"
#include "BaseWidget/IdaDialogMsg.h"
#include "BaseWidget/IWidgetSignal.h"
#include "DataManager/WindowTotalData.h"
#include "MainWindowSignalHandler.h"

namespace
{
    QString getColResultTypeAsStringInner(int fileResultType, bool isGainScalar)
    {
        QString resultDataType;
        switch (fileResultType)
        {
        case 0:
        case 1:
        case 2:
            resultDataType = QObject::tr("电场");
            break;
        case 3:
            resultDataType = QObject::tr("表面场分布");
            break;
        case 4:
            resultDataType = QObject::tr("表面电流密度");
            break;
        case 5:
            resultDataType = QObject::tr("空间电势分布");
            break;
        case 6:
            resultDataType = QObject::tr("表面电势分布");
            break;
        case 7:
            resultDataType = isGainScalar ? QObject::tr("增益") : QObject::tr("电场");
            break;
        case 8:
            resultDataType = QObject::tr("S参数");
            break;
        case 9:
            resultDataType = QObject::tr("驻波比");
            break;
        case 10:
            resultDataType = QObject::tr("电压");
            break;
        case 11:
            resultDataType = QObject::tr("电流");
            break;
        case 12:
            resultDataType = QObject::tr("干扰关联矩阵");
            break;
        case 13:
            resultDataType = QObject::tr("人体吸收比");
            break;
        case 14:
            resultDataType = QObject::tr("附着点路径");
            break;
        case 15:
            resultDataType = QObject::tr("面电势分布");
            break;
        }

        return resultDataType;
    }

} // namespace

namespace pst
{
    HeatMapWindow::HeatMapWindow(QWidget* parent, int windowId)
        : Graph2DWindowBase(parent), m_dataTimer(new QTimer(this)), m_animationSpeed{1.}, m_animationType{0} // 默认数据
          ,
          m_animationSettingData(new AnimationSettingData(this)), m_loopCount{0}, m_isExportingAnimation{false}, m_animationImageList{}, m_exportAnimationPath{"Animation.gif"}, m_imageSize{0}, m_windowId{windowId}, m_animationTooBarInfoData{nullptr}, m_dataHeatGraph2DSettings(new DataHeatGraph2DSettings(this))
    {
        m_tracerPropertyWidget = new TracerTableWidget(m_customPlot);
        m_tracerPropertyWidget->ShowValueProperty();
        m_tracerPropertyWidget->installEventFilter(this);
        
        connect(m_tracerPropertyWidget, &TracerTableWidget::signal_removeMarkerSelect, m_customPlot, &Curve2DPlotor::removeMarkerSelect);
        //connect(m_tracerPropertyWidget, &TracerTableWidget::signal_removeMarkerAll, m_customPlot, &Curve2DPlotor::removeMarkerAll);//删除全部marker会空指针，看后续哪个高手来解决了
        connect(m_tracerPropertyWidget, &TracerTableWidget::signal_disableTracer, m_customPlot, &Curve2DPlotor::disableTracer);
        connect(m_tracerPropertyWidget, &TracerTableWidget::signal_enableTracer, m_customPlot, &Curve2DPlotor::enableTracer);

        m_settings = new TwoDHeatMapSettings(this);
        m_settings->setMainWindowId(m_windowId);
        // 先连接信号和槽，然后设置m_settings的数值，以使其直接作用到m_customPlot
        initConnections();

        m_settings->updateUILayout(); // 布局
        /// @brief 测试代码结束

        // QSplitter* splitter = new QSplitter(Qt::Horizontal, this);
        // splitter->addWidget(m_customPlot);
        // splitter->addWidget(m_settings);
        // splitter->setSizes({ 3000, 1000 });
        QHBoxLayout* layout = new QHBoxLayout(this);
        layout->setContentsMargins(0, 0, 0, 0);
        layout->addWidget(m_customPlot);
        layout->addWidget(m_settings);
        layout->setStretch(0, 1);
        layout->setStretch(1, 0);
        // layout->addWidget(splitter);

        // 动图展示窗口
        m_animationTooBarInfoData = new AnimationTooBarInfoData(this);

        m_dataTimer->setInterval(1000. / 3);
        connect(m_dataTimer, &QTimer::timeout, this, &HeatMapWindow::slot_beginToPlayDataAnimation);

        /*
         * m_customPlot信号导出marker数据
         */
        connect(m_customPlot, &Curve2DPlotor::signal_exportMarkerData, this, &HeatMapWindow::slot_exportMarkerData);
    }

    HeatMapWindow::~HeatMapWindow()
    {
    }

    QDomElement& HeatMapWindow::writeToProjectFile(QDomDocument* doc, QDomElement* element, bool isdiso)
    {
        QDomElement _HeatMapWindowDataRoot = doc->createElement("GraphWindow");
        QDomAttr _WindowUniversalIDattr = doc->createAttribute("WindowUniversalID");
        _WindowUniversalIDattr.setValue(QString::number(m_windowUniversalID));
        _HeatMapWindowDataRoot.setAttributeNode(_WindowUniversalIDattr);
        QDomAttr _idattr = doc->createAttribute("WindowID");
        _idattr.setValue(QString::number(m_windowId));
        _HeatMapWindowDataRoot.setAttributeNode(_idattr);
        QDomAttr typeattr = doc->createAttribute("WindowType");
        typeattr.setValue("HeatMapWindow");
        _HeatMapWindowDataRoot.setAttributeNode(typeattr);

        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "AutoColorScale", QString::number(m_autoColorScale));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "AutoXRescaleAxes", QString::number(m_autoXRescaleAxes));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "AutoYRescaleAxes", QString::number(m_autoYRescaleAxes));
        QString _xAxisRangeStr = QString("%1,%2").arg(m_xAxisRange.lower).arg(m_xAxisRange.upper);
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "XAxisRange", _xAxisRangeStr);
        QString _yAxisRangeStr = QString("%1,%2").arg(m_yAxisRange.lower).arg(m_yAxisRange.upper);
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "YAxisRange", _yAxisRangeStr);
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "TracerIndex",QString::number(m_tracerIndex));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "AnimationType", QString::number(m_animationType));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "AnimationSpeed", QString::number(m_animationSpeed));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "IsExportingAnimation", QString::number(m_isExportingAnimation));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "LoopCount", QString::number(m_loopCount));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "ImageSize", QString::number(m_imageSize));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "ExportAnimationPath", m_exportAnimationPath);
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "AutoXTitleText", QString::number(m_autoXTitleText));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "AutoYTitleText", QString::number(m_autoYTitleText));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "XUnit", m_xUnit);
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "YUnit", m_yUnit);
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "IsBeginExportAnimation",  QString::number(m_isBeginExportAnimation));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "IsFirstTimeDataAnimation", QString::number(m_isFirstTimeDataAnimation));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "AnimationDataCount", QString::number(m_animationDataCount));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "IsCurveRenamed", QString::number(m_isCurveRenamed));
        GlobalHelperInstance->createDomElement(doc, &_HeatMapWindowDataRoot, "IsTitleRenamed", QString::number(m_isTitleRenamed));

        element->appendChild(_HeatMapWindowDataRoot);

        // m_curveMap存了所有曲线
        for (auto _curveInfoPair : m_curveMap)
        {
            auto _item = _curveInfoPair.first;
            _curveInfoPair.second->setIsCurveVisible(_item->checkState());
            _curveInfoPair.second->writeToProjectFile(doc, &_HeatMapWindowDataRoot, GraphType::TwoDHeatMap,isdiso);
        }
        if (m_tracerPropertyWidget != nullptr)
            m_tracerPropertyWidget->writeToProjectFile(doc, &_HeatMapWindowDataRoot, GraphType::TwoDHeatMap, isdiso);
        if (m_animationSettingData != nullptr)
           m_animationSettingData->writeToProjectFile(doc, &_HeatMapWindowDataRoot, isdiso);
        if (m_animationTooBarInfoData != nullptr)
            m_animationTooBarInfoData->writeToProjectFile(doc, &_HeatMapWindowDataRoot, isdiso);
        if (m_dataHeatGraph2DSettings != nullptr)
            m_dataHeatGraph2DSettings->writeToProjectFile(doc, &_HeatMapWindowDataRoot, isdiso);

        return _HeatMapWindowDataRoot;
    }

    void HeatMapWindow::readFromProjectFile(QDomElement* element, bool isdiso)
    {
        m_windowUniversalID = element->attribute("WindowUniversalID").toInt();
        int id = element->attribute("WindowID").toInt();
        m_windowId = id;

        QDomNodeList AutoColorScaleNodeList = element->elementsByTagName("AutoColorScale");
        m_autoColorScale = (bool)AutoColorScaleNodeList.at(0).toElement().text().toInt();
        QDomNodeList AutoXRescaleAxesNodeList = element->elementsByTagName("AutoXRescaleAxes");
        m_autoXRescaleAxes = (bool)AutoXRescaleAxesNodeList.at(0).toElement().text().toInt();
        QDomNodeList AutoYRescaleAxesNodeList = element->elementsByTagName("AutoYRescaleAxes");
        m_autoYRescaleAxes = (bool)AutoYRescaleAxesNodeList.at(0).toElement().text().toInt();
        QDomNodeList XAxisRangeList = element->elementsByTagName("XAxisRange");
        QString XAxisRangeStr = XAxisRangeList.at(0).toElement().text();
        m_xAxisRange.lower = XAxisRangeStr.split(",")[0].toDouble();
        m_xAxisRange.upper = XAxisRangeStr.split(",")[1].toDouble();
        QDomNodeList YAxisRangeList = element->elementsByTagName("YAxisRange");
        QString YAxisRangeStr = YAxisRangeList.at(0).toElement().text();
        m_yAxisRange.lower = YAxisRangeStr.split(",")[0].toDouble();
        m_yAxisRange.upper = YAxisRangeStr.split(",")[1].toDouble();
        QDomNodeList TracerIndexNodeList = element->elementsByTagName("TracerIndex");
        m_tracerIndex = TracerIndexNodeList.at(0).toElement().text().toInt();
        QDomNodeList AnimationTypeNodeList = element->elementsByTagName("AnimationType");
        m_animationType = AnimationTypeNodeList.at(0).toElement().text().toInt();
        QDomNodeList AnimationSpeedNodeList = element->elementsByTagName("AnimationSpeed");
        m_animationSpeed = AnimationSpeedNodeList.at(0).toElement().text().toDouble();
        QDomNodeList IsExportingAnimationNodeList = element->elementsByTagName("IsExportingAnimation");
        m_isExportingAnimation = (bool)IsExportingAnimationNodeList.at(0).toElement().text().toInt();
        QDomNodeList LoopCountNodeList = element->elementsByTagName("LoopCount");
        m_loopCount = LoopCountNodeList.at(0).toElement().text().toInt();
        QDomNodeList ImageSizeNodeList = element->elementsByTagName("ImageSize");
        m_imageSize = ImageSizeNodeList.at(0).toElement().text().toInt();
        QDomNodeList ExportAnimationPathNodeList = element->elementsByTagName("ExportAnimationPath");
        m_exportAnimationPath = ExportAnimationPathNodeList.at(0).toElement().text();
        QDomNodeList AutoXTitleTextNodeList = element->elementsByTagName("AutoXTitleText");
        m_autoXTitleText = (bool)AutoXTitleTextNodeList.at(0).toElement().text().toInt();
        QDomNodeList AutoYTitleTextNodeList = element->elementsByTagName("AutoYTitleText");
        m_autoYTitleText = (bool)AutoYTitleTextNodeList.at(0).toElement().text().toInt();
        QDomNodeList XUnitNodeList = element->elementsByTagName("XUnit");
        m_xUnit = XUnitNodeList.at(0).toElement().text();
        QDomNodeList YUnitNodeList = element->elementsByTagName("YUnit");
        m_yUnit = YUnitNodeList.at(0).toElement().text();
        QDomNodeList IsBeginExportAnimationNodeList = element->elementsByTagName("IsBeginExportAnimation");
        m_isBeginExportAnimation = (bool)IsBeginExportAnimationNodeList.at(0).toElement().text().toInt();
        QDomNodeList IsFirstTimeDataAnimationNodeList = element->elementsByTagName("IsFirstTimeDataAnimation");
        m_isFirstTimeDataAnimation = (bool)IsFirstTimeDataAnimationNodeList.at(0).toElement().text().toInt();
        QDomNodeList AnimationDataCountNodeList = element->elementsByTagName("AnimationDataCount");
        m_animationDataCount = AnimationDataCountNodeList.at(0).toElement().text().toInt();
        QDomNodeList IsCurveRenamedNodeList = element->elementsByTagName("IsCurveRenamed");
        m_isCurveRenamed = (bool)IsCurveRenamedNodeList.at(0).toElement().text().toInt();
        QDomNodeList IsTitleRenamedNodeList = element->elementsByTagName("IsTitleRenamed");
        m_isTitleRenamed = (bool)IsTitleRenamedNodeList.at(0).toElement().text().toInt();

        auto _AnimationSettingDataNodeList = element->elementsByTagName("AnimationSettingData");
        m_animationSettingData->readFromProjectFile(&_AnimationSettingDataNodeList.at(0).toElement(), isdiso);
        auto _AnimationTooBarInfoDataNodeList = element->elementsByTagName("AnimationTooBarInfoData");
        m_animationTooBarInfoData->readFromProjectFile(&_AnimationTooBarInfoDataNodeList.at(0).toElement(), isdiso);
        auto _DataHeatGraph2DSettingsNodeList = element->elementsByTagName("DataHeatGraph2DSettings");
        m_dataHeatGraph2DSettings->readFromProjectFile(&_DataHeatGraph2DSettingsNodeList.at(0).toElement(), isdiso);

        auto _infosNodeList = element->elementsByTagName("InfomationCurve2DBasical");
        auto _infosCount = _infosNodeList.count();
        if (_infosCount == 0)
            return;
        for (int i = 0; i < _infosCount; i++)
        {
            auto _info = Info2DPointer::create();
            _info->readFromProjectFile(&_infosNodeList.at(i).toElement(), GraphType::TwoDHeatMap, isdiso);
            if (_info->getProjectTreeSourceItem() == nullptr)
                continue;
            _info->setDataHeatGraph2DSettings(m_dataHeatGraph2DSettings);
            addGraph2D(_info);

            // 更新侧边栏和曲线数据
            auto model = qobject_cast<QStandardItemModel*>(m_settings->getCurveNameListView()->model());
            auto _curveSiderBarItem = this->getItemFromCurveInfo(_info);
            if (_curveSiderBarItem != nullptr)
            {
                auto _itemIndex = model->indexFromItem(_curveSiderBarItem);
                emit IBaseSignalInstance->signal_setHeatMapSelectedCurveNameIndexs(m_windowId, {_itemIndex.row()});
                if (_info->getIsNormalized() || _info->getIsDB())
                    emit IBaseSignalInstance->signal_setHeatMapDataOprationChanged(m_windowId, _info->getIsDB(), _info->getIsNormalized());
            }
            slot_ChangeHeatMapProperty(m_windowUniversalID, _info->getInformationID(), _info);
        }
        auto _TracerTableWidgetNodeList = element->elementsByTagName("TracerTableWidget");
        m_tracerPropertyWidget->readFromProjectFile(&_TracerTableWidgetNodeList, m_customPlot, GraphType::TwoDHeatMap, isdiso);
        if (m_tracerPropertyWidget->rowCount() > 0)
        {
            if (m_tracerPropertyWidget->isHidden())
            {
                m_tracerPropertyWidget->move(m_customPlot->axisRect()->topLeft() + QPoint(10, 10));
                m_tracerPropertyWidget->show();
            }
        }
        // 最后刷新标记的显示隐藏
        for (auto curvePair : m_curveMap)
        {
            auto _checkState = curvePair.second->getIsCurveVisible();
            if (!_checkState) // 默认可见，则不用设置
            {
                auto _curveSiderBarItem = getItemFromCurveInfo(curvePair.second);
                _curveSiderBarItem->model()->blockSignals(true);
                _curveSiderBarItem->setData(_checkState, Qt::UserRole);
                _curveSiderBarItem->model()->blockSignals(false);
                _curveSiderBarItem->setCheckState((Qt::CheckState)_checkState);
                slot_showHideItemActor(_curveSiderBarItem);
            }
        }
    }

    void HeatMapWindow::setWindowID(int windowID)
    {
        m_windowId = windowID;
    }
    int HeatMapWindow::getWindowID() const
    {
        return m_windowId;
    }

    void HeatMapWindow::setWindowUniversalID(int windowUniversalID)
    {
        m_windowUniversalID = windowUniversalID;
        m_settings->setWindowUniversalID(windowUniversalID);
    }
    int HeatMapWindow::getWindowUniversalID() const
    {
        return m_windowUniversalID;
    }

    void HeatMapWindow::addGraph2D(Info2DPointer& info)
    {
        m_customPlot->legend->setVisible(false);
        m_customPlot->axisRect()->setupFullAxesBox(true);

        // set up the QCPColorMap:
        QCPColorMap* colorMap = new QCPColorMap(m_customPlot->xAxis, m_customPlot->yAxis);

        m_graphs.append(colorMap);
        colorMap->setName(info->getYData().first);
        info->setCurveLineAddress(colorMap); // 记录曲线地址
        if (!GlobalHelperInstance->getIsOpenProject())
            info->setInformationID(WindowTotalDataInstance->getGraph2DInformationMaxID());
        WindowTotalDataInstance->appendInfomationCurve2DBasical(info);
        QStandardItem* item = m_settings->addCurveToList(info->getYData().first);
        info->setCurveName(info->getYData().first);
        // 添加曲线名字
        //{
        //    QSignalBlocker blocker(m_settings->getCurveNameModel());
        //    item->setCheckState(Qt::Checked);
        //}

        // m_curveMap[item] = info; // 记录下来以备查找
        addCurveItemInfoPair(item, info);
        info->setSidebarItem(item);
        // m_graphInfoList.append(info);
        qDebug() << "info.getReader() = " << info->getReader();

        QVector<double> column1, column2, column3;
        column1 = info->getReader()->getEachColDataOfKey().at(info->getReader()->getScanParameterIndex());
        column2 = info->getReader()->getEachColDataOfKey().at(info->getReader()->getSecondScanParameterIndex());
        column3 = info->getYData().second;
        SetHeatMapData(column1, column2, column3, colorMap);

        // add a color scale:
        QCPColorScale* colorScale = new QCPColorScale(m_customPlot);
        m_customPlot->plotLayout()->addElement(1, 1, colorScale); // add it to the right of the main axis rect
        colorScale->setType(QCPAxis::atRight);                    // scale shall be vertical bar with tick/axis labels right (actually atRight is already the default)
        colorMap->setColorScale(colorScale);                      // associate the color map with the color scale
        // colorScale->axis()->setLabel("Magnetic Field Strength");
        auto labelFonColorScale = colorScale->axis()->tickLabelFont();
        labelFonColorScale.setPointSize(16);
        labelFonColorScale.setFamily("Times New Roman");
        colorScale->axis()->setTickLabelFont(labelFonColorScale);
        auto selectLabelFonColorScale = colorScale->axis()->selectedTickLabelFont();
        selectLabelFonColorScale.setPointSize(16);
        colorScale->axis()->setSelectedTickLabelFont(selectLabelFonColorScale);
        // 模拟三维场图标量场的lookupTable
        QCPColorGradient gradient;
        gradient.clearColorStops();
        int numSteps = 150;
        for (int i = 0; i < numSteps; ++i)
        {
            double ratio = i / double(numSteps - 1);
            // 根据线性比例计算 hue 值，从 0.67（蓝）到 0.0（红）
            double hue = 0.67 * (1.0 - ratio) + 0.0 * ratio;
            gradient.setColorStopAt(ratio, QColor::fromHsvF(hue, 1.0, 1.0));
        }
        colorMap->setGradient(gradient);

        // rescale the data dimension (color) such that all data points lie in the span visualized by the color gradient:
        if (m_autoColorScale)
        {
            colorMap->rescaleDataRange(true);
        }

        // make sure the axis rect and color scale synchronize their bottom and top margins (so they line up):
        QCPMarginGroup* marginGroup = new QCPMarginGroup(m_customPlot);
        m_customPlot->axisRect()->setMarginGroup(QCP::MarginSides(QCP::msBottom | QCP::msTop), marginGroup);
        colorScale->setMarginGroup(QCP::MarginSides(QCP::msBottom | QCP::msTop), marginGroup);

        QString label;
        if (m_autoXTitleText)
        {
            slot_autoGetXTitleText(label);
        }
        else
        {
            label = m_customPlot->xAxis->label().remove(QRegularExpression("\\[.+]"));
        }
        m_xUnit = '[' + info->getReader()->getEachColUnitOfKey().at(info->getReader()->getScanParameterIndex()).trimmed() + ']';
        m_customPlot->xAxis->setLabel(label + m_xUnit);
        if (m_autoYTitleText)
        {
            slot_autoGetYTitleText(label);
        }
        else
        {
            label = m_customPlot->xAxis->label().remove(QRegularExpression("\\[.+]"));
        }
        m_yUnit = '[' + info->getReader()->getEachColUnitOfKey().at(info->getReader()->getSecondScanParameterIndex()).trimmed() + ']';
        m_customPlot->yAxis->setLabel(label + m_yUnit);

        slot_adaptiveRange();
        // m_customPlot->rescaleAxes();
        auto title = generateGraphNameAuto(info, false);
        m_customPlot->setWindowTitleName(title);

        
        m_customPlot->xAxis->setNumberFormat("f");
        m_customPlot->xAxis->setNumberPrecision(6);
        m_customPlot->yAxis->setNumberFormat("f");
        m_customPlot->yAxis->setNumberPrecision(6);


        m_customPlot->replot();
        auto _infoID = info->getInformationID();
        info->setDataHeatGraph2DSettings(m_dataHeatGraph2DSettings);
        slot_updateCurveData({_infoID}, _infoID);
        emit IWidgetSignalInstance->signal_updateAnimationButtonState();
    }

    void HeatMapWindow::slot_adaptiveRange()
    {
        if (m_autoXRescaleAxes)
        {
            m_customPlot->xAxis->rescale(true);
        }
        else
        {
            m_customPlot->xAxis->setRange(m_xAxisRange);
        }
        if (m_autoYRescaleAxes)
        {
            m_customPlot->yAxis->rescale(true);
        }
        else
        {
            m_customPlot->yAxis->setRange(m_yAxisRange);
        }
        m_customPlot->replot();
    }

    // 设置框中选中的曲线发生改变->根据曲线信息更新m_settings
    void HeatMapWindow::slot_chosenItemsChanged(const QModelIndexList& itemList)
    {
        QStandardItemModel* model = m_settings->getCurveNameModel();
        QList<Info2DPointer> curInfoList;
        for (const auto& i : itemList)
        {
            auto item = model->item(i.row());

            if (!judgmentItemInfoPairIsExist(item))
            {
                // todo 警告
                continue;
            }
            curInfoList.append(getCurveInfoByItem(item));
        }

        if (curInfoList.isEmpty())
        {
            // 折叠控制面板
            m_settings->hideCurvePropertylPanel();
        }
        else
        {
            // 各个曲线信息的交集
            Info2DPointer commonInfo = Info2DPointer::create();
            auto isSuc = InfomationCurve2DBasical::getCommonData(curInfoList, commonInfo);
            if (!isSuc)
            {
                // TODO 警告
                return;
            }
            m_settings->setCurvePropertyPanelState(commonInfo);
        }
    }

    void HeatMapWindow::setActiveAnimation(bool active)
    {
        m_isAnimationState = active;
        auto widget = m_settings->getCurveNameListView();
        if (m_animationType == 0) // 只响应动数据
        {
            m_dataTimer->setInterval(1000. / m_animationSettingData->frameRate() / m_animationSpeed);

            if (!active)
            {
                m_dataTimer->stop();
                //m_settings->setControlPanelEditable(true);
                widget->setFocusPolicy(Qt::StrongFocus);
                widget->setSelectionMode(QAbstractItemView::SingleSelection);
                // connectSignalsAndSlots();
                m_settings->setControlPlaneToAnimationState(false);
            }
            else
            {
                prepareAnimationData();
                m_dataTimer->start();
                widget->setFocusPolicy(Qt::NoFocus);
                widget->setSelectionMode(QAbstractItemView::NoSelection);
                //m_settings->setControlPanelEditable(false);
                m_settings->setControlPlaneToAnimationState(true);

                MainWindowSignalHandler::getInstance()->setToolButtonExportMotionGraphState(false);
                if (m_isExportingAnimation)
                {
                    emit IBaseSignalInstance->signal_UpdateStatusBarInformation(QStringLiteral("导出动图正在进行中..."), true);
                    QCoreApplication::processEvents();
                }
                // disconnectSignalsAndSlots();
            }
        }
    }

    void HeatMapWindow::exportAnimationData(const QString& path)
    {
        m_isExportingAnimation = true;
        m_loopCount = 0;
        m_animationImageList.clear();
        m_exportAnimationPath = path;
        m_isBeginExportAnimation = true;
        if (m_animationType == 0) // 动数据
        {
            // prepareAnimationData();
            setActiveAnimation(true);

            for (auto& infoPair : m_curveMap)
            {
                infoPair.second->setLastFrameIndexBeforeExportAnimation(infoPair.second->getCurrentAnimationIndex());
            }
        }
    }

    void HeatMapWindow::setAnimationSettingData(AnimationSettingData* data)
    {
        m_animationSettingData = data;
    }

    AnimationSettingData* HeatMapWindow::getAnimationSettingData() const
    {
        return m_animationSettingData;
    }

    void HeatMapWindow::setAnimationType(int type)
    {
        m_animationType = type;
    }

    int HeatMapWindow::getAnimationType() const
    {
        return m_animationType;
    }

    void HeatMapWindow::increaseAnimationSpeed()
    {
        if (m_animationSpeed < 2.0)
        {
            m_animationSpeed += 0.1;
        }
        m_animationSpeed = qMin(2.0, m_animationSpeed);
        m_dataTimer->setInterval(1000. / m_animationSettingData->frameRate() / m_animationSpeed);
        qDebug() << m_dataTimer->interval();
    }

    void HeatMapWindow::decreaseAnimationSpeed()
    {
        if (m_animationSpeed > 0.1)
        {
            m_animationSpeed -= 0.1;
        }
        m_animationSpeed = qMax(0.1, m_animationSpeed);
        m_dataTimer->setInterval(1000. / m_animationSettingData->frameRate() / m_animationSpeed);
        qDebug() << m_dataTimer->interval();
    }

    double HeatMapWindow::getSpeedFactor() const
    {
        return m_animationSpeed;
    }

    void HeatMapWindow::setSelectedCurveNameIndexs(QList<int> selectedCurveNameIndexs)
    {
        auto model = m_settings->getCurveNameModel();
        {
            QItemSelectionModel* selectionModel = m_settings->getCurveNameListView()->selectionModel();
            QSignalBlocker blocker(selectionModel); // 开始阻断 signal
            selectionModel->clearSelection();
            for (auto i : selectedCurveNameIndexs)
            {
                QModelIndex index = model->index(i, 0);
                selectionModel->select(index, QItemSelectionModel::Select);
            }
        }
        QList<QModelIndex> indexList;
        // 遍历 rowList ，使用 model->index(row, 0) 得到对应的 QModelIndex
        for (int row : selectedCurveNameIndexs)
        {
            QModelIndex index = model->index(row, 0); // 这里假设要访问的是第 0 列
            if (index.isValid())
            { // 检查是否有效
                indexList.append(index);
            }
        }
        slot_chosenItemsChanged(indexList);
        syncSelectedGraph();
    }

    bool HeatMapWindow::whetherCanEnableDataAnimation()
    {
        // auto infoList = m_curveMap.values();
        if (m_curveMap.size() != 1)
        {
            return false;
        }

        return isInfoContainsMultipleData(m_curveMap.at(0).second) ? true : false;
    }

    bool HeatMapWindow::isInfoContainsMultipleData(const Info2DPointer& info)
    {
        auto reader = info->getReader();
        if (reader == nullptr)
        {
            return false;
        }
        // 将当前帧数据设置回info
        auto readerList = info->getReader()->GetOtherList();

        return readerList.size() > 1;
    }

    void HeatMapWindow::setFixedValueChanged(QList<int> indexList)
    {
        m_settings->fixedValueChanged(indexList);
    }

    void HeatMapWindow::setComplexPartsChanged(bool isAmplitudeChecked, bool isPhaseChecked, bool isRealPartChecked, bool isImaginaryPartChecked)
    {
        m_settings->complexPartsChanged(isAmplitudeChecked, isPhaseChecked, isRealPartChecked, isImaginaryPartChecked);
    }

    void HeatMapWindow::setRemoveSelectedGraph()
    {
        m_settings->removeSelectedGraph();
    }

    void HeatMapWindow::setRenameFinished(QString name)
    {
        m_settings->renameFinished(name);
    }

    void HeatMapWindow::setVariableAxisChanged(int variableAxisIndex)
    {
        m_settings->variableAxisChanged(variableAxisIndex);
    }

    void HeatMapWindow::setSecondVariableAxisChanged(int variableAxisIndex)
    {
        m_settings->secondVariableAxisChanged(variableAxisIndex);
    }

    void HeatMapWindow::setCurveNameCheckStateChanged(int curveNameIndex, bool checkState)
    {
        m_settings->curveNameCheckStateChanged(curveNameIndex, checkState);
    }

    void HeatMapWindow::setDataOprationChanged(bool isLoged, bool isNormalized)
    {
        m_settings->setDataOprationChanged(isLoged, isNormalized);
    }

    void HeatMapWindow::setResultValueDataChanged(int valueIndex)
    {
        m_settings->setResultValueDataChanged(valueIndex);
    }

    void HeatMapWindow::saveHeatMapMainSettingData(bool scalarBarIsAutoRange, double scalarBarMin,
                                                   double scalarBarMax, QString scalarBarColor, int scalarBarFontSize, bool XAxisAutoTitle, QString XAxisTitle,
                                                   QString XTitleFontColor, int XTitleFontSize, QString XAxisColor, double XAisThickness,
                                                   int XValuePrecision, QString XAxisDigitColor, int XAxisDigitSize, bool XAxisIsAutoRange,
                                                   double XAxisMax, double XAxisMin, bool XIsAutoStepSize, double XCustomStepSize, bool YAxisAutoTitle, QString YAxisTitle,
                                                   QString YTitleFontColor, int YTitleFontSize, QString YAxisColor, double YAisThickness,
                                                   int YValuePrecision, QString YAxisDigitColor, int YAxisDigitSize, bool YAxisIsAutoRange,
                                                   double YAxisMax, double YAxisMin, bool YIsAutoStepSize, double YCustomStepSize, bool gridIsMainGridLineDisplay,
                                                   QString gridMainGridLineColor, int gridMainGridLineStyle, double gridMainGridLineThickness,
                                                   bool gridIsSecondaryGridLineDisplay, QString gridSecondaryGridLineColor, int gridSecondaryGridLineStyle,
                                                   double gridSecondaryGridLineThickness, QString windowTitleName, QString windowTitleFontColor, int windowTitleFontSize)
    {
        DataHeatGraph2DSettings* data = new DataHeatGraph2DSettings(this);

        auto _colorBar = data->getColorBar();
        auto _xAxisDisplay = data->getXAxisDisplay();
        auto _xAxisRange = data->getXAxisRange();
        auto _yAxisDisplay = data->getYAxisDisplay();
        auto _yAxisRange = data->getYAxisRange();
        auto _grid = data->getGrid();
        auto _title = data->getTitle();

        QList<int> scalarBarColorList = GlobalHelperInstance->convertStringToIntList(scalarBarColor);
        QColor _scalarBarColor = QColor(scalarBarColorList.at(0), scalarBarColorList.at(1), scalarBarColorList.at(2));
        QList<int> XTitleFontColorList = GlobalHelperInstance->convertStringToIntList(XTitleFontColor);
        QColor _XTitleFontColor = QColor(XTitleFontColorList.at(0), XTitleFontColorList.at(1), XTitleFontColorList.at(2));
        QList<int> XAxisColorList = GlobalHelperInstance->convertStringToIntList(XAxisColor);
        QColor _XAxisColor = QColor(XAxisColorList.at(0), XAxisColorList.at(1), XAxisColorList.at(2));
        QList<int> XAxisDigitColorList = GlobalHelperInstance->convertStringToIntList(XAxisDigitColor);
        QColor _XAxisDigitColor = QColor(XAxisDigitColorList.at(0), XAxisDigitColorList.at(1), XAxisDigitColorList.at(2));
        QList<int> YTitleFontColorList = GlobalHelperInstance->convertStringToIntList(YTitleFontColor);
        QColor _YTitleFontColor = QColor(YTitleFontColorList.at(0), YTitleFontColorList.at(1), YTitleFontColorList.at(2));
        QList<int> YAxisColorList = GlobalHelperInstance->convertStringToIntList(YAxisColor);
        QColor _YAxisColor = QColor(YAxisColorList.at(0), YAxisColorList.at(1), YAxisColorList.at(2));
        QList<int> YAxisDigitColorList = GlobalHelperInstance->convertStringToIntList(YAxisDigitColor);
        QColor _YAxisDigitColor = QColor(YAxisDigitColorList.at(0), YAxisDigitColorList.at(1), YAxisDigitColorList.at(2));
        QList<int> gridMainGridLineColorList = GlobalHelperInstance->convertStringToIntList(gridMainGridLineColor);
        QColor _gridMainGridLineColor = QColor(gridMainGridLineColorList.at(0), gridMainGridLineColorList.at(1), gridMainGridLineColorList.at(2));
        QList<int> gridSecondaryGridLineColorList = GlobalHelperInstance->convertStringToIntList(gridSecondaryGridLineColor);
        QColor _gridSecondaryGridLineColor = QColor(gridSecondaryGridLineColorList.at(0), gridSecondaryGridLineColorList.at(1), gridSecondaryGridLineColorList.at(2));
        QList<int> windowTitleFontColorList = GlobalHelperInstance->convertStringToIntList(windowTitleFontColor);
        QColor _windowTitleFontColor = QColor(windowTitleFontColorList.at(0), windowTitleFontColorList.at(1), windowTitleFontColorList.at(2));

        _colorBar->m_isAutoRange = scalarBarIsAutoRange;
        _colorBar->m_maxValue = scalarBarMax;
        _colorBar->m_minValue = scalarBarMin;
        _colorBar->m_fontColor = _scalarBarColor;
        _colorBar->m_fontSize = scalarBarFontSize;

        _xAxisDisplay->m_isAutoName = XAxisAutoTitle;
        _xAxisDisplay->m_titleText = XAxisTitle;
        _xAxisDisplay->m_titleFontColor = _XTitleFontColor;
        _xAxisDisplay->m_titleFontSize = XTitleFontSize;
        _xAxisDisplay->m_axisColor = _XAxisColor;
        _xAxisDisplay->m_axisThickness = XAisThickness;
        _xAxisDisplay->m_valuePrecision = XValuePrecision;
        _xAxisDisplay->m_axisDigitColor = _XAxisDigitColor;
        _xAxisDisplay->m_axisDigitSize = XAxisDigitSize;

        _xAxisRange->m_isAutoRange = XAxisIsAutoRange;
        _xAxisRange->m_maxValue = XAxisMax;
        _xAxisRange->m_minValue = XAxisMin;
        _xAxisRange->m_isAutoStepSize = XIsAutoStepSize;
        _xAxisRange->m_customStepSize = XCustomStepSize;

        _yAxisDisplay->m_isAutoName = YAxisAutoTitle;
        _yAxisDisplay->m_titleText = YAxisTitle;
        _yAxisDisplay->m_titleFontColor = _YTitleFontColor;
        _yAxisDisplay->m_titleFontSize = YTitleFontSize;
        _yAxisDisplay->m_axisColor = _YAxisColor;
        _yAxisDisplay->m_axisThickness = YAisThickness;
        _yAxisDisplay->m_valuePrecision = YValuePrecision;
        _yAxisDisplay->m_axisDigitColor = _YAxisDigitColor;
        _yAxisDisplay->m_axisDigitSize = YAxisDigitSize;

        _yAxisRange->m_isAutoRange = YAxisIsAutoRange;
        _yAxisRange->m_maxValue = YAxisMax;
        _yAxisRange->m_minValue = YAxisMin;
        _yAxisRange->m_isAutoStepSize = YIsAutoStepSize;
        _yAxisRange->m_customStepSize = YCustomStepSize;

        _grid->m_isMainGridLineDisplay = gridIsMainGridLineDisplay;
        _grid->m_mainGridLineColor = _gridMainGridLineColor;
        _grid->m_mainGridLineStyle = gridMainGridLineStyle;
        _grid->m_mainGridLineThickness = gridMainGridLineThickness;
        _grid->m_isSecondaryGridLineDisplay = gridIsSecondaryGridLineDisplay;
        _grid->m_secondaryGridLineColor = _gridSecondaryGridLineColor;
        _grid->m_secondaryGridLineStyle = gridSecondaryGridLineStyle;
        _grid->m_secondaryGridLineThickness = gridSecondaryGridLineThickness;

        _title->m_titleName = windowTitleName;
        _title->m_titleFontColor = _windowTitleFontColor;
        _title->m_titleFontSize = windowTitleFontSize;

        changeHeatMapProperty(data);
    }

    void HeatMapWindow::changeHeatMapProperty(const DataHeatGraph2DSettings* dataGraph2DSettings)
    {
        if (dataGraph2DSettings == nullptr)
        {
            return;
        }
        auto tmp = dataGraph2DSettings;
        //tmp.copyData(dataGraph2DSettings);

        // colorScale
        auto colorScale = qobject_cast<QCPColorScale*>(m_customPlot->plotLayout()->element(1, 1));
        if (colorScale != nullptr)
        {
            this->SetColorScaleFlag(tmp->getColorBar()->m_isAutoRange);
            if (this->GetColorScaleFlag())
            {
                if (m_customPlot->plottableCount() > 0)
                {
                    auto colorMap = qobject_cast<QCPColorMap*>(m_customPlot->plottable(0));
                    colorMap->rescaleDataRange(true);
                }
            }
            else
            {
                auto rangeColorScale = colorScale->dataRange();
                rangeColorScale.upper = tmp->getColorBar()->m_maxValue;
                rangeColorScale.lower = tmp->getColorBar()->m_minValue;
                colorScale->setDataRange(rangeColorScale);
            }
            colorScale->axis()->setTickLabelColor(tmp->getColorBar()->m_fontColor);
            auto labelFonColorScale = colorScale->axis()->tickLabelFont();
            labelFonColorScale.setPointSize(tmp->getColorBar()->m_fontSize);
            colorScale->axis()->setTickLabelFont(labelFonColorScale);
            auto selectLabelFonColorScale = colorScale->axis()->selectedTickLabelFont();
            selectLabelFonColorScale.setPointSize(tmp->getColorBar()->m_fontSize);
            colorScale->axis()->setSelectedTickLabelFont(selectLabelFonColorScale);
        }

        // x
        QString xTitleText;
        this->SetXTitleAutoFlag(tmp->getXAxisDisplay()->m_isAutoName);
        if (this->GetXTitleAutoFlag())
        {
            slot_autoGetXTitleText(xTitleText);
        }
        else
        {
            xTitleText = tmp->getXAxisDisplay()->m_titleText;
        }
        m_customPlot->xAxis->setLabel(xTitleText + m_xUnit);
        m_customPlot->xAxis->setLabelColor(tmp->getXAxisDisplay()->m_titleFontColor);
        auto labelFontX = m_customPlot->xAxis->labelFont();
        labelFontX.setPointSize(tmp->getXAxisDisplay()->m_titleFontSize);
        m_customPlot->xAxis->setLabelFont(labelFontX);
        m_customPlot->xAxis->setSelectedLabelFont(labelFontX);
        auto basePenX = m_customPlot->xAxis->basePen();
        basePenX.setColor(tmp->getXAxisDisplay()->m_axisColor);
        basePenX.setWidthF(tmp->getXAxisDisplay()->m_axisThickness);
        m_customPlot->xAxis->setBasePen(basePenX);
        auto selectBasePenX = m_customPlot->xAxis->selectedBasePen();
        selectBasePenX.setWidthF(tmp->getXAxisDisplay()->m_axisThickness);
        m_customPlot->xAxis->setSelectedBasePen(selectBasePenX);
        m_customPlot->xAxis->setNumberPrecision(tmp->getXAxisDisplay()->m_valuePrecision);
        m_customPlot->xAxis->setTickLabelColor(tmp->getXAxisDisplay()->m_axisDigitColor);
        auto tickLabelFontX = m_customPlot->xAxis->tickLabelFont();
        tickLabelFontX.setPointSize(tmp->getXAxisDisplay()->m_axisDigitSize);
        m_customPlot->xAxis->setTickLabelFont(tickLabelFontX);
        auto selectTickLabelFontX = m_customPlot->xAxis->selectedTickLabelFont();
        selectTickLabelFontX.setPointSize(tmp->getXAxisDisplay()->m_axisDigitSize);
        m_customPlot->xAxis->setSelectedTickLabelFont(selectTickLabelFontX);

        // xRange
        this->SetXAutoFlag(tmp->getXAxisRange()->m_isAutoRange);
        if (this->GetXAutoFlag())
        {
            m_customPlot->xAxis->rescale(true);
        }
        else
        {
            auto rangeX = m_customPlot->xAxis->range();
            rangeX.upper = tmp->getXAxisRange()->m_maxValue;
            rangeX.lower = tmp->getXAxisRange()->m_minValue;
            m_customPlot->xAxis->setRange(rangeX);
            m_xAxisRange = rangeX;
        }
        if (tmp->getXAxisRange()->m_isAutoStepSize)
        {
            m_customPlot->xAxis->setTicker(QSharedPointer<QCPAxisTicker>(new QCPAxisTicker()));
        }
        else
        {
            QSharedPointer<QCPAxisTickerFixed> fixed(QSharedPointer<QCPAxisTickerFixed>(new QCPAxisTickerFixed()));
            fixed->setTickStep(tmp->getXAxisRange()->m_customStepSize);
            fixed->setScaleStrategy(QCPAxisTickerFixed::ScaleStrategy::ssNone);
            m_customPlot->xAxis->setTicker(fixed);
        }

        // y
        QString yTitleText;
        this->SetYTitleAutoFlag(tmp->getYAxisDisplay()->m_isAutoName);
        if (this->GetYTitleAutoFlag())
        {
            slot_autoGetYTitleText(yTitleText);
        }
        else
        {
            yTitleText = tmp->getYAxisDisplay()->m_titleText;
        }
        m_customPlot->yAxis->setLabel(yTitleText + m_yUnit);
        m_customPlot->yAxis->setLabelColor(tmp->getYAxisDisplay()->m_titleFontColor);
        auto labelFontY = m_customPlot->yAxis->labelFont();
        labelFontY.setPointSize(tmp->getYAxisDisplay()->m_titleFontSize);
        m_customPlot->yAxis->setLabelFont(labelFontY);
        m_customPlot->yAxis->setSelectedLabelFont(labelFontY);
        auto basePenY = m_customPlot->yAxis->basePen();
        basePenY.setColor(tmp->getYAxisDisplay()->m_axisColor);
        basePenY.setWidthF(tmp->getYAxisDisplay()->m_axisThickness);
        m_customPlot->yAxis->setBasePen(basePenY);
        auto selectBasePenY = m_customPlot->yAxis->selectedBasePen();
        selectBasePenY.setColor(tmp->getYAxisDisplay()->m_axisColor);
        selectBasePenY.setWidthF(tmp->getYAxisDisplay()->m_axisThickness);
        m_customPlot->yAxis->setSelectedBasePen(selectBasePenY);
        m_customPlot->yAxis->setNumberPrecision(tmp->getYAxisDisplay()->m_valuePrecision);
        m_customPlot->yAxis->setTickLabelColor(tmp->getYAxisDisplay()->m_axisDigitColor);
        auto tickLabelFontY = m_customPlot->yAxis->tickLabelFont();
        tickLabelFontY.setPointSize(tmp->getYAxisDisplay()->m_axisDigitSize);
        m_customPlot->yAxis->setTickLabelFont(tickLabelFontY);
        auto selectTickLabelFontY = m_customPlot->yAxis->selectedTickLabelFont();
        selectTickLabelFontY.setPointSize(tmp->getYAxisDisplay()->m_axisDigitSize);
        m_customPlot->yAxis->setSelectedTickLabelFont(selectTickLabelFontY);

        // yRange
        this->SetYAutoFlag(tmp->getYAxisRange()->m_isAutoRange);
        if (this->GetYAutoFlag())
        {
            m_customPlot->yAxis->rescale(true);
        }
        else
        {
            auto rangeY = m_customPlot->yAxis->range();
            rangeY.upper = tmp->getYAxisRange()->m_maxValue;
            rangeY.lower = tmp->getYAxisRange()->m_minValue;
            m_customPlot->yAxis->setRange(rangeY);
            m_yAxisRange = rangeY;
        }
        if (tmp->getYAxisRange()->m_isAutoStepSize)
        {
            m_customPlot->yAxis->setTicker(QSharedPointer<QCPAxisTicker>(new QCPAxisTicker()));
        }
        else
        {
            QSharedPointer<QCPAxisTickerFixed> fixed(QSharedPointer<QCPAxisTickerFixed>(new QCPAxisTickerFixed()));
            fixed->setTickStep(tmp->getYAxisRange()->m_customStepSize);
            fixed->setScaleStrategy(QCPAxisTickerFixed::ScaleStrategy::ssNone);
            m_customPlot->yAxis->setTicker(fixed);
        }

        // grid
        m_customPlot->xAxis->grid()->setVisible(tmp->getGrid()->m_isMainGridLineDisplay);
        m_customPlot->yAxis->grid()->setVisible(tmp->getGrid()->m_isMainGridLineDisplay);
        auto penGrid = m_customPlot->xAxis->grid()->pen();
        penGrid.setColor(tmp->getGrid()->m_mainGridLineColor);
        penGrid.setStyle(Qt::PenStyle(tmp->getGrid()->m_mainGridLineStyle + 1));
        penGrid.setWidthF(tmp->getGrid()->m_mainGridLineThickness);
        m_customPlot->xAxis->grid()->setPen(penGrid);
        m_customPlot->yAxis->grid()->setPen(penGrid);
        m_customPlot->xAxis->grid()->setSubGridVisible(tmp->getGrid()->m_isSecondaryGridLineDisplay);
        m_customPlot->yAxis->grid()->setSubGridVisible(tmp->getGrid()->m_isSecondaryGridLineDisplay);
        auto subGridPen = m_customPlot->xAxis->grid()->subGridPen();
        subGridPen.setColor(tmp->getGrid()->m_secondaryGridLineColor);
        subGridPen.setStyle(Qt::PenStyle(tmp->getGrid()->m_secondaryGridLineStyle + 1));
        subGridPen.setWidthF(tmp->getGrid()->m_secondaryGridLineThickness);
        m_customPlot->xAxis->grid()->setSubGridPen(subGridPen);
        m_customPlot->yAxis->grid()->setSubGridPen(subGridPen);

        // title
        auto title = qobject_cast<QCPTextElement*>(m_customPlot->plotLayout()->element(0, 0));
        if (title != nullptr)
        {
            title->setText(tmp->getTitle()->m_titleName);
            title->setTextColor(tmp->getTitle()->m_titleFontColor);
            auto fontTitle = title->font();
            fontTitle.setPointSize(tmp->getTitle()->m_titleFontSize);
            title->setFont(fontTitle);
            title->setSelectedFont(fontTitle);
        }

        m_customPlot->replot();
    }

    void HeatMapWindow::slot_generateANewGraphCurve(const QModelIndexList& itemList, const Info2DPointer& changedInfo)
    {
        QList<Info2DPointer> newDataList;

        for (const auto& i : itemList)
        {
            QStandardItemModel* model = m_settings->getCurveNameModel();
            auto item = model->item(i.row());
            if (!judgmentItemInfoPairIsExist(item))
            {
                // todo 警告
                continue;
            }

            auto tempInfo = getCurveInfoByItem(item);

            Info2DPointer dataInfo = Info2DPointer::create();
            *dataInfo.get() = *tempInfo.get();

            auto _graph = dataInfo->getGraph();
            auto _reader = dataInfo->getReader();
            auto* curveAdd = dataInfo->getCurveLineAddress();

            if (curveAdd == nullptr || _graph == nullptr || _reader == nullptr)
            {
                // TODO 警告
                return;
            }

            auto keyNames = _reader->getEachColNameOfKey();
            auto keyDataList = _reader->getEachColDataOfKey();

            auto changedScanName = changedInfo->getScanParameterName();
            auto newScanIndex = keyNames.indexOf(changedScanName);
            auto oldScanIndex = dataInfo->getScanParameterIndex();
            // 设置新的扫描参数
            if (newScanIndex == -1)
            {
                // TODO 警告
                return;
            }
            dataInfo->setScanParameterIndex(newScanIndex);

            auto secondChangedScanName = changedInfo->getSecondScanParameterName();
            auto secondNewScanIndex = keyNames.indexOf(secondChangedScanName);
            auto secondOldScanIndex = dataInfo->getSecondScanParameterIndex();
            // 设置新的扫描参数
            if (secondNewScanIndex == -1)
            {
                // TODO 警告
                return;
            }
            dataInfo->setSecondScanParameterIndex(secondNewScanIndex);

            // 设置新的otherKeyList
            QVector<double> temp = dataInfo->getOtherKeyData();
            QVector<double> otherKeyVector(temp.size() + 2);
            for (int index = 0; index < otherKeyVector.size(); ++index)
            {
                if (index == oldScanIndex)
                {
                    otherKeyVector[index] = keyDataList[oldScanIndex][0];
                }
                else if (index == secondOldScanIndex)
                {
                    otherKeyVector[index] = keyDataList[secondOldScanIndex][0];
                }
                else
                {
                    otherKeyVector[index] = temp.takeFirst();
                }
            }
            auto minMax = std::minmax(newScanIndex, secondNewScanIndex);
            otherKeyVector.removeAt(minMax.second);
            otherKeyVector.removeAt(minMax.first);

            _reader->setScanParameterIndex(newScanIndex);
            _reader->setSecondScanParameterIndex(secondNewScanIndex);

            QVector<QVector<double>> tempOtherKey;
            for (auto i : otherKeyVector)
            {
                tempOtherKey.append(QVector<double>{i});
            }
            _reader->setOtherKeyParameter(tempOtherKey);
            QList<Info2DPointer> newYList;
            _reader->getChosenLinesFromMultiKey(QList<QPair<int, int>>{{dataInfo->getValueIndex(), dataInfo->getValueComponent()}}, newYList);

            for (auto& i : newYList)
            {
                i->setGraphType(dataInfo->getGraphType());
                i->setVarValueList(changedInfo->getVarValueList());
                i->setUnit(changedInfo->getUnit());
                i->setFieldType(changedInfo->getFieldType());
                i->setProjectTreeSourceItem(changedInfo->getProjectTreeSourceItem());
                newDataList.append(i);
            }
        }

        emit signal_generateANewGraphCurve(newDataList);
    }

    void HeatMapWindow::slot_HeatMapPropertyShow(QCPColorMap* plottable, int index)
    {
        emit IWidgetSignalInstance->signal_exitAddMarkerState();
        //DataHeatGraph2DSettings tmp;

        // colorScale
        auto colorScale = qobject_cast<QCPColorScale*>(m_customPlot->plotLayout()->element(1, 1));
        if (colorScale != nullptr)
        {
            //m_dataHeatGraph2DSettings->getColorBar()->m_isAutoRange = this->GetColorScaleFlag();
            m_dataHeatGraph2DSettings->getColorBar()->m_maxValue = colorScale->dataRange().upper;
            m_dataHeatGraph2DSettings->getColorBar()->m_minValue = colorScale->dataRange().lower;
            //tmp.getColorBar()->m_fontColor = colorScale->axis()->tickLabelColor();
            //tmp.getColorBar()->m_fontSize = colorScale->axis()->tickLabelFont().pointSize();
        }

        auto tickerX = dynamic_cast<QCPAxisTickerFixed*>(m_customPlot->xAxis->ticker().data());
        if (tickerX == nullptr)
        {
            m_dataHeatGraph2DSettings->getXAxisRange()->m_isAutoStepSize = true;
            m_dataHeatGraph2DSettings->getXAxisRange()->m_customStepSize = 0.01;
            auto tickerVec = m_customPlot->xAxis->tickVector();
            if (tickerVec.size() >= 2)
            {
                m_dataHeatGraph2DSettings->getXAxisRange()->m_customStepSize = tickerVec[1] - tickerVec[0];
            }
        }

        auto tickerY = dynamic_cast<QCPAxisTickerFixed*>(m_customPlot->yAxis->ticker().data());
        if (tickerY == nullptr)
        {
            m_dataHeatGraph2DSettings->getYAxisRange()->m_isAutoStepSize = true;
            m_dataHeatGraph2DSettings->getYAxisRange()->m_customStepSize = 0.01;
            auto tickerVec = m_customPlot->yAxis->tickVector();
            if (tickerVec.size() >= 2)
            {
                m_dataHeatGraph2DSettings->getYAxisRange()->m_customStepSize = tickerVec[1] - tickerVec[0];
            }
        }
        m_dataHeatGraph2DSettings->getXAxisRange()->m_maxValue = m_customPlot->xAxis->range().upper;
        m_dataHeatGraph2DSettings->getXAxisRange()->m_minValue = m_customPlot->xAxis->range().lower;
        m_dataHeatGraph2DSettings->getYAxisRange()->m_maxValue = m_customPlot->yAxis->range().upper;
        m_dataHeatGraph2DSettings->getYAxisRange()->m_minValue = m_customPlot->yAxis->range().lower;


        //// x
        //tmp.getXAxisDisplay()->m_isAutoName = this->GetXTitleAutoFlag();
        //tmp.getXAxisDisplay()->m_titleText = m_customPlot->xAxis->label().remove(QRegularExpression("\\[.+]"));
        //tmp.getXAxisDisplay()->m_titleFontColor = m_customPlot->xAxis->labelColor();
        //tmp.getXAxisDisplay()->m_titleFontSize = m_customPlot->xAxis->labelFont().pointSize();
        //tmp.getXAxisDisplay()->m_axisColor = m_customPlot->xAxis->basePen().color();
        //tmp.getXAxisDisplay()->m_axisThickness = m_customPlot->xAxis->basePen().widthF();
        //tmp.getXAxisDisplay()->m_valuePrecision = m_customPlot->xAxis->numberPrecision();
        //tmp.getXAxisDisplay()->m_axisDigitColor = m_customPlot->xAxis->tickLabelColor();
        //tmp.getXAxisDisplay()->m_axisDigitSize = m_customPlot->xAxis->tickLabelFont().pointSize();

        //// xRange
        //tmp.getXAxisRange()->m_isAutoRange = this->GetXAutoFlag();
        //if (tmp.getXAxisRange()->m_isAutoRange)
        //{
            //tmp.getXAxisRange()->m_maxValue = m_customPlot->xAxis->range().upper;
            //tmp.getXAxisRange()->m_minValue = m_customPlot->xAxis->range().lower;
        //}
        //else
        //{
        //    tmp.getXAxisRange()->m_maxValue = m_xAxisRange.upper;
        //    tmp.getXAxisRange()->m_minValue = m_xAxisRange.lower;
        //}
        //auto tickerX = dynamic_cast<QCPAxisTickerFixed*>(m_customPlot->xAxis->ticker().data());
        //if (tickerX == nullptr)
        //{
        //    tmp.getXAxisRange()->m_isAutoStepSize = true;
        //    auto tickerVec = m_customPlot->xAxis->tickVector();
        //    if (tickerVec.size() >= 2)
        //    {
        //        tmp.getXAxisRange()->m_customStepSize = tickerVec[1] - tickerVec[0];
        //    }
        //    else
        //    {
        //        tmp.getXAxisRange()->m_customStepSize = 0.01;
        //    }
        //}
        //else
        //{
        //    tmp.getXAxisRange()->m_isAutoStepSize = false;
        //    tmp.getXAxisRange()->m_customStepSize = tickerX->tickStep();
        //}

        //// y
        //tmp.getYAxisDisplay()->m_isAutoName = this->GetYTitleAutoFlag();
        //tmp.getYAxisDisplay()->m_titleText = m_customPlot->yAxis->label().remove(QRegularExpression("\\[.+]"));
        //tmp.getYAxisDisplay()->m_titleFontColor = m_customPlot->yAxis->labelColor();
        //tmp.getYAxisDisplay()->m_titleFontSize = m_customPlot->yAxis->labelFont().pointSize();
        //tmp.getYAxisDisplay()->m_axisColor = m_customPlot->yAxis->basePen().color();
        //tmp.getYAxisDisplay()->m_axisThickness = m_customPlot->yAxis->basePen().widthF();
        //tmp.getYAxisDisplay()->m_valuePrecision = m_customPlot->yAxis->numberPrecision();
        //tmp.getYAxisDisplay()->m_axisDigitColor = m_customPlot->yAxis->tickLabelColor();
        //tmp.getYAxisDisplay()->m_axisDigitSize = m_customPlot->yAxis->tickLabelFont().pointSize();

        //// yRange
        //tmp.getYAxisRange()->m_isAutoRange = this->GetYAutoFlag();
        //if (tmp.getYAxisRange()->m_isAutoRange)
        //{
        //    tmp.getYAxisRange()->m_maxValue = m_customPlot->yAxis->range().upper;
        //    tmp.getYAxisRange()->m_minValue = m_customPlot->yAxis->range().lower;
        //}
        //else
        //{
        //    tmp.getYAxisRange()->m_maxValue = m_yAxisRange.upper;
        //    tmp.getYAxisRange()->m_minValue = m_yAxisRange.lower;
        //}
        //auto tickerY = dynamic_cast<QCPAxisTickerFixed*>(m_customPlot->yAxis->ticker().data());
        //if (tickerY == nullptr)
        //{
        //    tmp.getYAxisRange()->m_isAutoStepSize = true;
        //    auto tickerVec = m_customPlot->yAxis->tickVector();
        //    if (tickerVec.size() >= 2)
        //    {
        //        tmp.getYAxisRange()->m_customStepSize = tickerVec[1] - tickerVec[0];
        //    }
        //    else
        //    {
        //        tmp.getYAxisRange()->m_customStepSize = 0.01;
        //    }
        //}
        //else
        //{
        //    tmp.getYAxisRange()->m_isAutoStepSize = false;
        //    tmp.getYAxisRange()->m_customStepSize = tickerY->tickStep();
        //}

        //// grid
        //tmp.getGrid()->m_isMainGridLineDisplay = m_customPlot->xAxis->grid()->visible();
        //tmp.getGrid()->m_mainGridLineColor = m_customPlot->xAxis->grid()->pen().color();
        //tmp.getGrid()->m_mainGridLineStyle = m_customPlot->xAxis->grid()->pen().style() - 1;
        //tmp.getGrid()->m_mainGridLineThickness = m_customPlot->xAxis->grid()->pen().widthF();
        //tmp.getGrid()->m_isSecondaryGridLineDisplay = m_customPlot->xAxis->grid()->subGridVisible();
        //tmp.getGrid()->m_secondaryGridLineColor = m_customPlot->xAxis->grid()->subGridPen().color();
        //tmp.getGrid()->m_secondaryGridLineStyle = m_customPlot->xAxis->grid()->subGridPen().style() - 1;
        //tmp.getGrid()->m_secondaryGridLineThickness = m_customPlot->xAxis->grid()->subGridPen().widthF();

        //// title
        //auto title = qobject_cast<QCPTextElement*>(m_customPlot->plotLayout()->element(0, 0));
        //if (title != nullptr)
        //{
        //    tmp.getTitle()->m_titleName = title->text();
        //    tmp.getTitle()->m_titleFontColor = title->textColor();
        //    tmp.getTitle()->m_titleFontSize = title->font().pointSize();
        //}

        auto _dialog = new DialogHeatGraph2DSettings(this);
        
        if (nullptr != m_doubleClickPlottable && m_graphs.contains(m_doubleClickPlottable))
        {
            for (auto _curvePair : m_curveMap)
            {
                if (_curvePair.second->getCurveLineAddress() == m_doubleClickPlottable)
                {
                    _dialog->setCurveInfoID(_curvePair.second->getInformationID());

                    if (!m_isTitleRenamed)
                    {
                        auto title = generateGraphNameAuto(_curvePair.second, _curvePair.second->getIsDB());
                        m_dataHeatGraph2DSettings->getTitle()->m_titleName = title;
                    }
                    break;
                }
            }
        }

        m_graphs;

        connect(_dialog, &DialogHeatGraph2DSettings::signal_sendData, [=](int windowUniversalID, int curveInfoID, Info2DPointer info) 
            {
                    slot_ChangeHeatMapProperty(windowUniversalID, curveInfoID, info);
                    auto colorScale = qobject_cast<QCPColorScale*>(m_customPlot->plotLayout()->element(1, 1));
                    if (colorScale != nullptr)
                    {
                        m_dataHeatGraph2DSettings->getColorBar()->m_maxValue = colorScale->dataRange().upper;
                        m_dataHeatGraph2DSettings->getColorBar()->m_minValue = colorScale->dataRange().lower;
                    }
                    m_dataHeatGraph2DSettings->getXAxisRange()->m_maxValue = m_customPlot->xAxis->range().upper;
                    m_dataHeatGraph2DSettings->getXAxisRange()->m_minValue = m_customPlot->xAxis->range().lower;
                    m_dataHeatGraph2DSettings->getYAxisRange()->m_maxValue = m_customPlot->yAxis->range().upper;
                    m_dataHeatGraph2DSettings->getYAxisRange()->m_minValue = m_customPlot->yAxis->range().lower;
           
                    //x轴步长
                    //auto tickerX = m_customPlot->xAxis->ticker();
                    //if (static_cast<QCPAxisTickerFixed*>(tickerX) != nullptr)//固定步长
                    //{
                    //    m_dataHeatGraph2DSettings->getXAxisRange()->m_customStepSize = static_cast<QCPAxisTickerFixed*>(tickerX)->tickStep();
                    //}
                    //else
                    //{
                    auto lablesX = m_customPlot->xAxis->tickVectorLabels();
                    if (lablesX.size() > 1)
                    {
                        m_dataHeatGraph2DSettings->getXAxisRange()->m_customStepSize = std::abs(lablesX[0].toDouble() - lablesX[1].toDouble());
                    }
                    //}

                    //y轴步长
                    //auto tickerY = m_customPlot->yAxis->ticker();
                    //if (static_cast<QCPAxisTickerFixed*>(tickerY) != nullptr)//固定步长
                    //{
                    //    m_dataHeatGraph2DSettings->getYAxisRange()->m_customStepSize = qobject_cast<QCPAxisTickerFixed*>(tickerY)->tickStep();
                    //}
                    //else
                    //{
                    auto lablesY = m_customPlot->yAxis->tickVectorLabels();
                    if (lablesY.size() > 1)
                    {
                        m_dataHeatGraph2DSettings->getYAxisRange()->m_customStepSize = std::abs(lablesY[0].toDouble() - lablesY[1].toDouble());
                    }
                    //}

                    _dialog->initData(m_dataHeatGraph2DSettings);
            });
        connect(_dialog, &DialogHeatGraph2DSettings::signal_titleRename, this, [this]() { m_isTitleRenamed = true; });
        _dialog->setWindowUniversalID(m_windowUniversalID);
        _dialog->initData(m_dataHeatGraph2DSettings);
        _dialog->ChangeCurrentWidget(index);
        _dialog->showWindow();
    }

    void HeatMapWindow::slot_ChangeHeatMapProperty(int windowUniversalID, int curveInfoID, Info2DPointer info)
    {
        DataHeatGraph2DSettings* tempPolarCenterSettingData = nullptr;
        if (curveInfoID != -1)
        {
            auto _curveInfo = WindowTotalDataInstance->getInfomationCurve2DBasicalByID(curveInfoID);
            tempPolarCenterSettingData = _curveInfo->getDataHeatGraph2DSettings();
            if (tempPolarCenterSettingData == nullptr)
                return;
        }
        else
        {
            auto _HeatMapWindow = (HeatMapWindow*)WindowTotalDataInstance->getGraph2DWindowByID(windowUniversalID);
            tempPolarCenterSettingData = _HeatMapWindow->getDataHeatGraph2DSettings();
            if (tempPolarCenterSettingData == nullptr)
                return;
        }
        changeHeatMapProperty(tempPolarCenterSettingData);
    }

    void HeatMapWindow::slot_showHideItemActor(QStandardItem* item)
    {
        if (item == nullptr)
            return;

        const auto& info = getCurveInfoByItem(item);
        auto plottable = info->getCurveLineAddress();
        if (plottable == nullptr)
            return;
        plottable->setVisible(item->checkState());
        QCPColorMap* graph = qobject_cast<QCPColorMap*>(info->getCurveLineAddress());
        auto colorScale = graph->colorScale();
        colorScale->setVisible(item->checkState()); // 保持布局，只隐藏

        auto tracerPropertyWidget = m_tracerPropertyWidget;
        for (int i = 0; i < tracerPropertyWidget->rowCount(); ++i)
        {
            auto tracer = tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
            if (tracer->GetLayerable() == plottable)
            {
                tracer->setVisible(item->checkState());
            }
        }
        m_customPlot->replot();
    }

    void HeatMapWindow::slot_AddTracer()
    {
        auto isShowPreviewTracer = m_customPlot->GetIsShowPreviewTracer();
        if (!isShowPreviewTracer)
        {
            return;
        }
        auto previewTracer = m_customPlot->GetPreviewTracer();
        if (previewTracer == nullptr || !previewTracer->visible())
        {
            return;
        }
        auto tracer = new LineChartTracer(m_customPlot);
        auto _graph = previewTracer->GetLayerable();
        tracer->setInterpolating(previewTracer->interpolating());
        tracer->setStyle(previewTracer->style());
        tracer->setPen(previewTracer->pen());
        tracer->setBrush(previewTracer->brush());
        tracer->setSize(previewTracer->size());
        tracer->SetLayerable(_graph);
        tracer->SetValue(previewTracer->GetValue());
        tracer->position->setCoords(previewTracer->position->coords());
        tracer->updatePosition();
        tracer->setVisible(true);
        QString name = "Marker" + QString::number(++m_tracerIndex);
        auto existNames = getMarkerNameMap().values();
        for (int i = 0; i < INT_MAX; ++i)
        {
            if (!existNames.contains(name))
            {
                break;
            }
            name = "Marker" + QString::number(++m_tracerIndex);
        }
        tracer->setTracerIndex(m_tracerIndex);
        tracer->SetName(name);
        for (auto _curveInfoPair : m_curveMap)
        {
            if (_curveInfoPair.second->getCurveLineAddress() == _graph)
            {
                tracer->setCurveInfoID(_curveInfoPair.second->getInformationID());
            }
        }
        auto tracerPropertyWidget = m_tracerPropertyWidget;
        if (tracerPropertyWidget->isHidden())
        {
            tracerPropertyWidget->move(m_customPlot->axisRect()->topLeft() + QPoint(10, 10));
            tracerPropertyWidget->show();
        }
        tracerPropertyWidget->AppendTracerProperty(tracer);
        auto tmp = tracer->getStyle2DTracerProperty();
        tmp->m_tracerName = tracer->GetName();
        tmp->m_x = tracer->position->coords().x();
        tmp->m_y = tracer->position->coords().y();
        tmp->m_symbolStyle = tracer->style() - 1;
        tmp->m_symbolSize = tracer->size() / 2;
        tmp->m_symbolColor = tracer->pen().color();
        // previewTracer->setVisible(false);
        // m_customPlot->SetIsShowPreviewTracer(false);
        m_customPlot->replot();

        QColor penColor = previewTracer->pen().color();
        int penR = penColor.red();
        int penG = penColor.green();
        int penB = penColor.blue();
        int penA = penColor.alpha();

        QStringList _pyCodes{};
        _pyCodes += QString("twodlinechart = PostProcessing.TwoDLineChart()");
        _pyCodes += QString("twodlinechart.setAddTracer(%1,'%2',%3,%4,%5,%6,%7,%8,%9,%10,%11,%12,%13,%14)").arg((int)m_windowId).arg(name).arg(tracer->position->coords().x()).arg(tracer->position->coords().y()).arg(previewTracer->GetValue())
            .arg((int)previewTracer->style()).arg(previewTracer->size()).arg(penR).arg(penG).arg(penB).arg(penA).arg(previewTracer->interpolating()).arg(tracer->getCurveInfoID()).arg(m_tracerIndex);
        emit IBaseSignalInstance->signal_execPYCode(_pyCodes, true, false);
    }

    void HeatMapWindow::pyAddTracer(QString name, double x, double y, double value, int tracerStyle, int size, int penR, int penG, int penB, int penA,
        int interpolating, int curveInfoID, int tracerIndex)
    {
        Info2DPointer infoPtr = WindowTotalDataInstance->getInfomationCurve2DBasicalByID(curveInfoID);
        auto _graph = infoPtr->getCurveLineAddress();

        auto tracer = new LineChartTracer(m_customPlot);
        tracer->setInterpolating(interpolating);
        tracer->setStyle((QCPItemTracer::TracerStyle) tracerStyle);
        tracer->setPen(QColor(penR, penG, penB, penA));
        //tracer->setBrush(previewTracer->brush());
        tracer->setSize(size);
        tracer->SetLayerable(_graph);
        tracer->SetValue(value);
        tracer->position->setCoords(QPointF(x, y));
        tracer->updatePosition();
        tracer->setVisible(true);
        tracer->setTracerIndex(tracerIndex);
        tracer->SetName(name);
        tracer->setCurveInfoID(curveInfoID);
        auto tracerPropertyWidget = m_tracerPropertyWidget;
        if (tracerPropertyWidget->isHidden())
        {
            tracerPropertyWidget->move(m_customPlot->axisRect()->topLeft() + QPoint(10, 10));
            tracerPropertyWidget->show();
        }
        tracerPropertyWidget->AppendTracerProperty(tracer);
        auto tmp = tracer->getStyle2DTracerProperty();
        tmp->m_tracerName = tracer->GetName();
        tmp->m_x = tracer->position->coords().x();
        tmp->m_y = tracer->position->coords().y();
        tmp->m_symbolStyle = tracer->style() - 1;
        tmp->m_symbolSize = tracer->size() / 2;
        tmp->m_symbolColor = tracer->pen().color();

        m_customPlot->replot();
    }

    void HeatMapWindow::slot_ShowPreviewTracer(QMouseEvent* event)
    {
        if (!m_customPlot->GetIsShowPreviewTracer())
        {
            return;
        }
        auto previewTracer = m_customPlot->GetPreviewTracer();
        if (previewTracer == nullptr)
        {
            return;
        }
        double x_value = m_customPlot->xAxis->pixelToCoord(event->pos().x());
        double y_value = m_customPlot->yAxis->pixelToCoord(event->pos().y());
        for (int i = 0; i < m_customPlot->plottableCount(); ++i)
        {
            auto colorMap = qobject_cast<QCPColorMap*>(m_customPlot->plottable(i));
            auto colorData = colorMap->data();
            auto keyRange = colorData->keyRange();
            auto valueRange = colorData->valueRange();
            auto keySize = colorData->keySize();
            auto valueSize = colorData->valueSize();
            int keyCell = int((x_value - keyRange.lower) / (keyRange.upper - keyRange.lower) * (keySize - 1) + 0.5);
            int valueCell = int((y_value - valueRange.lower) / (valueRange.upper - valueRange.lower) * (valueSize - 1) + 0.5);
            if (keyCell >= 0 && keyCell < keySize && valueCell >= 0 && valueCell < valueSize)
            {
                double xTmp, yTmp;
                colorData->cellToCoord(keyCell, valueCell, &xTmp, &yTmp);
                previewTracer->setVisible(true);
                previewTracer->SetLayerable(colorMap);
                previewTracer->position->setCoords(xTmp, yTmp);
                previewTracer->SetValue(colorData->cell(keyCell, valueCell));
                previewTracer->updatePosition();
            }
            else
            {
                previewTracer->setVisible(false);
            }
        }
        m_customPlot->replot();
    }

    void HeatMapWindow::slot_removeSelectedTracer(const QList<LineChartTracer*>& selectTracers)
    {
        QList<int> selectTracersID;
        for (auto tracer : selectTracers)
        {
            selectTracersID.append(tracer->getTracerIndex());
        }

        QString _selectTracersInfoID = GlobalHelperInstance->convertIntListToQString(selectTracersID);

        QStringList _pyCode;
        _pyCode.append(QString("twodlinechart = PostProcessing.TwoDLineChart()"));
        _pyCode.append(QString("twodlinechart.setRemoveSelectedTracer(%1,\"%2\")").arg(m_windowId).arg(_selectTracersInfoID));
        emit IBaseSignalInstance->signal_execPYCode(_pyCode, true, false);

        emit IBaseSignalInstance->signal_setHeatMapRemoveSelectedTracer(m_windowId, selectTracersID);
    }

    void HeatMapWindow::pyRemoveSelectedTracer(QList<int> selectTracersID)
    {
        for (auto tracersID : selectTracersID)
        {
            //Info2DPointer infoPtr = WindowTotalDataInstance->getInfomationCurve2DBasicalByID(curveInfoID);
            //QCPColorMap* graph = qobject_cast<QCPColorMap*>(infoPtr->getCurveLineAddress());

            auto tracerPropertyWidget = m_tracerPropertyWidget;
            for (int i = 0; i < tracerPropertyWidget->rowCount(); ++i)
            {
                auto t = tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
                if (t->getTracerIndex() == tracersID)
                {
                    tracerPropertyWidget->removeRow(i);
                    m_customPlot->removeItem(t);
                    break;
                }
            }
            if (tracerPropertyWidget->rowCount() == 0)
            {
                tracerPropertyWidget->hide();
            }
        }
        m_customPlot->replot();
    }

    void HeatMapWindow::slot_removeAllTracer(const QList<LineChartTracer*>& tracers)
    {
        QList<int> selectTracersInfoID;
        for (auto tracer : tracers)
        {
            selectTracersInfoID.append(tracer->getCurveInfoID());
        }

        QString _selectTracersInfoID = GlobalHelperInstance->convertIntListToQString(selectTracersInfoID);

        QStringList _pyCode;
        _pyCode.append(QString("twodlinechart = PostProcessing.TwoDLineChart()"));
        _pyCode.append(QString("twodlinechart.setRemoveAllTracer(%1,\"%2\")").arg(m_windowId).arg(_selectTracersInfoID));
        emit IBaseSignalInstance->signal_execPYCode(_pyCode, true, false);

        emit IBaseSignalInstance->signal_setHeatMapRemoveAllTracer(m_windowId, selectTracersInfoID);
        
    }

    void HeatMapWindow::pyRemoveAllTracer(QList<int> selectTracersInfoID)
    {
        selectTracersInfoID = QList<int>::fromSet(QSet<int>::fromList(selectTracersInfoID));
        auto tracerPropertyWidget = m_tracerPropertyWidget;

        for (auto curveInfoID : selectTracersInfoID)
        {
            for (int i = 0; i < tracerPropertyWidget->rowCount(); ++i)
            {
                auto t = tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
                if (t->getCurveInfoID() == curveInfoID)
                {
                    m_customPlot->removeItem(t);
                }
            }
        }

        tracerPropertyWidget->clearContents();
        tracerPropertyWidget->setRowCount(0);
        tracerPropertyWidget->hide();
        m_customPlot->replot();
    }

    void HeatMapWindow::slot_TracerPropertyShow(LineChartTracer* tracer)
    {
        if (tracer == nullptr)
        {
            return;
        }
        emit IWidgetSignalInstance->signal_exitAddMarkerState();
        auto tmp = tracer->getStyle2DTracerProperty();

        //m_doubleClickTracer = tracer;

        auto _dialog = new DialogTracer2DSettings(this);
        _dialog->setTracerID(tracer->getTracerIndex());
        connect(_dialog, &DialogTracer2DSettings::signal_sendData, this, &HeatMapWindow::slot_ChangeTracerProperty);

        QHash<int, QString> map = getMarkerNameMap();
        for (auto it = map.cbegin(); it != map.cend(); ++it)
        {
            if (it.value() == tracer->GetName())
            {
                _dialog->setCurrentMarkerIndex(it.key());
                break;
            }
        }
        _dialog->setExistMarkerMap(map);
        _dialog->initData({tmp, nullptr});
        _dialog->EnableHeatmapTracer(true);
        _dialog->ChangeCurrentWidget(0);
        _dialog->showWindow();
    }

    void HeatMapWindow::slot_ChangeTracerProperty(int tracerID, int guideLineID)
    {
        Style2D_TracerProperty* tmp = nullptr;
        for (int i = 0; i < m_tracerPropertyWidget->rowCount(); ++i)
        {
            auto tracer = m_tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
            if (tracer->getTracerIndex() == tracerID)
            {
                tmp = tracer->getStyle2DTracerProperty();
                if (tmp == nullptr)
                    return;
                tracer->SetName(tmp->m_tracerName);
                tracer->setStyle(QCPItemTracer::TracerStyle(tmp->m_symbolStyle + 1));
                tracer->setSize(tmp->m_symbolSize * 2);
                tracer->setPen(QPen(tmp->m_symbolColor));
                break;
            }
        }

        m_customPlot->replot();

        QStringList _pyCode;
        _pyCode.append(QString("twodlinechart = PostProcessing.TwoDLineChart()"));
        _pyCode.append(QString("twodlinechart.setChangeTracerProperty(%1,\"%2\",%3,%4,%5,%6,%7,%8,%9)").arg(m_windowId).arg(tmp->m_tracerName).arg(tmp->m_symbolStyle).arg(tmp->m_symbolSize)
            .arg(tmp->m_symbolColor.red()).arg(tmp->m_symbolColor.green()).arg(tmp->m_symbolColor.blue()).arg(tmp->m_symbolColor.alpha()).arg(tracerID));
        emit IBaseSignalInstance->signal_execPYCode(_pyCode, true, false);
    }

    void HeatMapWindow::pyChangeTracerProperty(QString name, int symbolStyle, int size, int penR, int penG, int penB, int penA, int tracerID)
    {
        for (int i = 0; i < m_tracerPropertyWidget->rowCount(); ++i)
        {
            auto tracer = m_tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
            if (tracer->getTracerIndex() == tracerID)
            {
                tracer->SetName(name);
                tracer->setStyle(QCPItemTracer::TracerStyle(symbolStyle + 1));
                tracer->setSize(size * 2);
                tracer->setPen(QPen(QColor(penR, penG, penB, penA)));
                break;
            }
        }

        m_customPlot->replot();
    }

    void HeatMapWindow::slot_updateHeatmapTracerProperty()
    {
        if (m_tracerPropertyWidget->isHidden())
        {
            return;
        }
        for (int i = 0; i < m_tracerPropertyWidget->rowCount(); ++i)
        {
            auto tracer = m_tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
            if (tracer->visible())
            {
                auto colorMap = qobject_cast<QCPColorMap*>(m_customPlot->plottable());
                if (colorMap)
                {
                    tracer->SetValue(colorMap->data()->data(tracer->position->coords().x(), tracer->position->coords().y()));
                }
            }
        }
    }

    void HeatMapWindow::slot_beginToPlayDataAnimation()
    {
        animationToNextDataFrame();

        // 每次都从m_animationSettingData更新数据
        m_dataTimer->setInterval(1000. / m_animationSettingData->frameRate() / m_animationSpeed);
        if (m_isExportingAnimation) // 导出动画状态
        {
            // 先判断是否应该结束,再截图
            if (m_loopCount * m_animationSettingData->sampleRate() >= m_imageSize)
            {
                setActiveAnimation(false);
                m_isExportingAnimation = false;
                setFrameToBeforeExportAnimation();
                exportAnimationToLocal();
                return;
            }
            ++m_loopCount;
            QPixmap pixmap = m_customPlot->toPixmap();
            cv::Mat image = CommonFunction::QPixmapToCVMat(pixmap);
            m_animationImageList.append(image);
        }
    }

    void HeatMapWindow::slot_getSidebarCurveDataSource(QStandardItem* item, QString& dataSource)
    {
        Info2DPointer info = getCurveInfoByItem(item);
        emit IWidgetSignalInstance->signal_getTwoDDataSources(info->getReader(), dataSource);
    }

    void HeatMapWindow::slot_autoGetXTitleText(QString& xTitleText)
    {
        if (m_curveMap.isEmpty())
        {
            return;
        }
        const auto& info = m_curveMap.at(0).second;
        QString unit = info->getReader()->getEachColUnitOfKey().at(info->getReader()->getScanParameterIndex()).trimmed();
        if (unit == "MHz")
        {
            xTitleText = "Frequency";
        }
        else if (unit == "us")
        {
            xTitleText = "Time";
        }
        else
        {
            xTitleText = info->getReader()->getEachColNameOfKey().at(info->getReader()->getScanParameterIndex()).trimmed();
        }
    }

    void HeatMapWindow::slot_autoGetYTitleText(QString& yTitleText)
    {
        if (m_curveMap.isEmpty())
        {
            return;
        }
        const auto& info = m_curveMap.at(0).second;
        QString unit = info->getReader()->getEachColUnitOfKey().at(info->getReader()->getSecondScanParameterIndex()).trimmed();
        if (unit == "MHz")
        {
            yTitleText = "Frequency";
        }
        else if (unit == "us")
        {
            yTitleText = "Time";
        }
        else
        {
            yTitleText = info->getReader()->getEachColNameOfKey().at(info->getReader()->getSecondScanParameterIndex()).trimmed();
        }
    }

    void HeatMapWindow::initConnections()
    {
        connect(m_settings, &TwoDHeatMapSettings::signal_generateANewGraphCurve,
                this, &HeatMapWindow::slot_generateANewGraphCurve);

        connect(m_settings, &TwoDHeatMapSettings::signal_updateCurveData,
                this, &HeatMapWindow::slot_updateCurveData);

        connect(m_settings, &TwoDHeatMapSettings::signal_legendTitleFontFamilyChanged,
                this, &HeatMapWindow::slot_legendTitleFontFamilyChanged);
        connect(m_settings, &TwoDHeatMapSettings::signal_legendTitleFontSizeChanged,
                this, &HeatMapWindow::slot_legendTitleFontSizeChanged);

        connect(m_settings, &TwoDHeatMapSettings::signal_renameGraph,
                this, &HeatMapWindow::slot_renameGraph);

        connect(m_settings, &TwoDHeatMapSettings::signal_removeSelectedGraph, this, &HeatMapWindow::slot_removeSelectedGraph);
        connect(m_customPlot, &Curve2DPlotor::signal_updateSelectedGraph, this, &HeatMapWindow::slot_updateSelectedGraph);
        connect(m_customPlot, &Curve2DPlotor::signal_removeSelectedGraph, this, &HeatMapWindow::slot_removeSelectedGraph);

        connect(m_settings, &TwoDHeatMapSettings::signal_renameFinished, this, &HeatMapWindow::slot_renameFinished);

        connect(m_settings, &TwoDHeatMapSettings::signal_getSidebarCurveDataSource, this, &HeatMapWindow::slot_getSidebarCurveDataSource);

        connect(m_customPlot, &QCustomPlot::axisDoubleClick, [this](QCPAxis* axis, QCPAxis::SelectablePart part, QMouseEvent* event)
                {
                if (axis == m_customPlot->xAxis)
                {
                    slot_HeatMapPropertyShow(nullptr, 1);
                }
                else if (axis == m_customPlot->yAxis)
                {
                    slot_HeatMapPropertyShow(nullptr, 3);
                }
                else
                {
                    auto colorScale = qobject_cast<QCPColorScale*>(m_customPlot->plotLayout()->element(1, 1));
                    if (colorScale != nullptr)
                    {
                        if (axis->axisRect() == colorScale->axis()->axisRect())
                        {
                            slot_HeatMapPropertyShow(nullptr, 0);
                        }
                    }
                } });

        auto title = qobject_cast<QCPTextElement*>(m_customPlot->plotLayout()->element(0, 0));
        if (title != nullptr)
        {
            connect(title, &QCPTextElement::doubleClicked, [this](QMouseEvent* event)
                    { slot_HeatMapPropertyShow(nullptr, 5); });
        }


        auto listView = m_settings->getCurveNameListView();
        if (listView != nullptr)
        {
            connect(listView, &QListView::doubleClicked, [this](const QModelIndex& index)
                {
                    if (m_isAnimationState) return;
                    auto model = qobject_cast<QStandardItemModel*>(m_settings->getCurveNameListView()->model());
                    auto item = model->itemFromIndex(index);
                    QCPColorMap* graph = qobject_cast<QCPColorMap*>(getCurveInfoByItem(item)->getCurveLineAddress());
                    if (m_customPlot->xAxis != nullptr)
                    {
                        if (graph != nullptr)
                        {
                            m_doubleClickPlottable = graph;
                            slot_HeatMapPropertyShow(nullptr, 0);
                        }
                    } });
        }

        connect(m_settings, &TwoDHeatMapSettings::signal_showHideItemActor,
                this, &HeatMapWindow::slot_showHideItemActor);

        connect(m_customPlot, &QCustomPlot::itemDoubleClick, [this](QCPAbstractItem* item, QMouseEvent* event)
                {
                        auto tracerPropertyWidget = m_tracerPropertyWidget;
                        for (int i = 0; i < tracerPropertyWidget->rowCount(); ++i)
                        {
                            auto tracer = tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value< LineChartTracer* >();
                            if (tracer == item || tracer->GetLabel() == item || tracer->GetArrow() == item)
                            {
                                slot_TracerPropertyShow(tracer);
                                return;
                            }
                        } });
        connect(m_tracerPropertyWidget, &TracerTableWidget::signal_doubleClicked, this, &HeatMapWindow::slot_TracerPropertyShow);

        connect(m_customPlot, &Curve2DPlotor::signal_addTracer, this, &HeatMapWindow::slot_AddTracer);
        connect(m_customPlot, &Curve2DPlotor::mouseMove, this, &HeatMapWindow::slot_ShowPreviewTracer);
        connect(m_customPlot, &Curve2DPlotor::signal_removeSelectedTracer, this, &HeatMapWindow::slot_removeSelectedTracer);
        connect(m_customPlot, &Curve2DPlotor::signal_removeAlldTracer, this, &HeatMapWindow::slot_removeAllTracer);
        connect(m_customPlot, &QCustomPlot::afterReplot, this, &HeatMapWindow::slot_updateHeatmapTracerProperty);
        connect(m_customPlot, &QCustomPlot::afterReplot, m_tracerPropertyWidget, &TracerTableWidget::slot_updateTracerProperty);
        connect(m_customPlot, &QCustomPlot::selectionChangedByUser, m_tracerPropertyWidget, &TracerTableWidget::slot_updateTracerChange);
        connect(IWidgetSignalInstance, &BaseWidget::IWidgetSignal::signal_closeTree, this, &HeatMapWindow::slot_closeTree);
    }

    void HeatMapWindow::slot_renameGraph()
    {
        auto listView = m_settings->getCurveNameListView();
        QModelIndexList selectedIndexes = listView->selectionModel()->selectedIndexes();
        if (selectedIndexes.size() != 1)
        {
            return;
        }
        emit IWidgetSignalInstance->signal_exitAddMarkerState();
        listView->setCurrentIndex(selectedIndexes.first());
        listView->edit(selectedIndexes.first());
    }

    void HeatMapWindow::slot_renameFinished(QString name)
    {
        m_isCurveRenamed = true;
        auto listView = m_settings->getCurveNameListView();
        QModelIndexList selectedIndexes = listView->selectionModel()->selectedIndexes();
        if (selectedIndexes.size() != 1)
        {
            return;
        }
        //auto name = listView->model()->data(selectedIndexes.first()).toString();
        bool success = listView->model()->setData(selectedIndexes.first(), name);
        for (auto curvePair : m_curveMap)
        {
            auto info = curvePair.second;
            QCPColorMap* graph = qobject_cast<QCPColorMap*>(info->getCurveLineAddress());
            if (graph->selected())
            {
                info->setCurveName(name);
            }
        }
        //m_customPlot->setWindowTitleName(name);
        m_customPlot->replot();
    }

    void HeatMapWindow::slot_updateSelectedGraph()
    {
        return;
        // disconnect(m_settings, &TwoDHeatMapSettings::signal_chosenItemsChanged,
        //     this, &HeatMapWindow::slot_chosenItemsChanged);
        QList<Info2DPointer> curInfoList;

        auto model = qobject_cast<QStandardItemModel*>(m_settings->getCurveNameListView()->model());
        auto selectionModel = m_settings->getCurveNameListView()->selectionModel();
        for (auto curvePair : m_curveMap)
        {
            auto info = curvePair.second;
            QCPColorMap* graph = qobject_cast<QCPColorMap*>(info->getCurveLineAddress());
            if (graph->selected())
            {
                QModelIndex index = model->indexFromItem(curvePair.first);
                selectionModel->select(index, QItemSelectionModel::Select);
                curInfoList.append(info);
            }
            else
            {
                QModelIndex index = model->indexFromItem(curvePair.first);
                selectionModel->select(index, QItemSelectionModel::Deselect);
            }
        }

        // 设置框中选中的曲线发生改变->根据曲线信息更新m_settings
        if (curInfoList.isEmpty())
        {
            // 折叠控制面板
            m_settings->hideCurvePropertylPanel();
        }
        else
        {
            // 各个曲线信息的交集
            Info2DPointer commonInfo = Info2DPointer::create();
            auto isSuc = InfomationCurve2DBasical::getCommonData(curInfoList, commonInfo);
            if (!isSuc)
            {
                // TODO 警告
                return;
            }
            m_settings->setCurvePropertyPanelState(commonInfo);
        }
        // connect(m_settings, &Graph2DSettings::signal_chosenItemsChanged,
        //     this, &HeatMapWindow::slot_chosenItemsChanged);
    }

    void HeatMapWindow::syncSelectedGraph()
    {
        auto model = qobject_cast<QStandardItemModel*>(m_settings->getCurveNameListView()->model());
        auto selectionModel = m_settings->getCurveNameListView()->selectionModel();
        for (auto curvePair : m_curveMap)
        {
            auto item = curvePair.first;
            QModelIndex index = model->indexFromItem(curvePair.first);
            if (selectionModel->isSelected(index))
            {
                QCPColorMap* graph = qobject_cast<QCPColorMap*>(curvePair.second->getCurveLineAddress());
                int total = graph->data()->keySize() * graph->data()->valueSize();
                graph->setSelection(QCPDataSelection(QCPDataRange(0, total - 1)));
            }
        }
    }

    void HeatMapWindow::slot_removeSelectedGraph()
    {
        disconnect(m_customPlot, &Curve2DPlotor::signal_updateSelectedGraph, this, &HeatMapWindow::slot_updateSelectedGraph);

        auto model = qobject_cast<QStandardItemModel*>(m_settings->getCurveNameListView()->model());
        auto selectionModel = m_settings->getCurveNameListView()->selectionModel();
        QList<QStandardItem*> removeList;
        QList<LineChartTracer*> tracerList;
        for (auto curvePair : m_curveMap)
        {
            auto info = curvePair.second;
            QCPColorMap* graph = qobject_cast<QCPColorMap*>(info->getCurveLineAddress());
            if (graph->selected())
            {
                QModelIndex index = model->indexFromItem(curvePair.first);
                model->removeRow(index.row());
                auto tracerPropertyWidget = m_tracerPropertyWidget;
                for (int i = 0; i < tracerPropertyWidget->rowCount(); ++i)
                {
                    auto tracer = tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
                    tracerList.append(tracer); // 热力图只有一个图，全删
                }

                info->setCurveLineAddress(nullptr);
                auto colorScale = graph->colorScale();
                colorScale->setVisible(false); // 保持布局，只隐藏
                m_customPlot->removePlottable(graph);
                removeList.append(curvePair.first);
            }
        }
        slot_removeSelectedTracer(tracerList);

        for (auto i : removeList)
        {
            auto _infoID = getCurveInfoByItem(i)->getInformationID();
            WindowTotalDataInstance->removeInfomationCurve2DBasicalByID(_infoID);
            removeCurveItemInfoPair(i);
        }
        // m_customPlot->replot();

        connect(m_customPlot, &Curve2DPlotor::signal_updateSelectedGraph, this, &HeatMapWindow::slot_updateSelectedGraph);

        m_customPlot->SetIsShowPreviewTracer(false); // 退出标记状态
        m_customPlot->GetPreviewTracer()->setVisible(false);
        m_customPlot->replot();
        emit IWidgetSignalInstance->signal_updateAnimationButtonState();
    }

    void HeatMapWindow::slot_removeGraphs(const QList<QString>& graphNameList)
    {
        disconnect(m_customPlot, &Curve2DPlotor::signal_updateSelectedGraph, this, &HeatMapWindow::slot_updateSelectedGraph);

        auto model = qobject_cast<QStandardItemModel*>(m_settings->getCurveNameListView()->model());
        auto selectionModel = m_settings->getCurveNameListView()->selectionModel();
        QList<QStandardItem*> removeList;
        QList<LineChartTracer*> tracerList;
        for (auto curvePair : m_curveMap)
        {
            auto info = curvePair.second;
            QCPColorMap* graph = qobject_cast<QCPColorMap*>(info->getCurveLineAddress());
            if (graphNameList.contains(graph->name()))
            {
                QModelIndex index = model->indexFromItem(curvePair.first);
                model->removeRow(index.row());
                auto tracerPropertyWidget = m_tracerPropertyWidget;
                for (int i = 0; i < tracerPropertyWidget->rowCount(); ++i)
                {
                    auto tracer = tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
                    //    if (tracer->graph() == graph)
                    //    {
                    //        tracerList.append(tracer);
                    //    }
                }
                ////auto measureTableWidget = m_measureTableWidget;
                ////for (int i = 0; i < measureTableWidget->rowCount(); ++i)
                ////{
                ////    auto tracer = measureTableWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
                ////    if (tracer->GetLayerable() == graph)
                ////    {
                ////        tracerList.append(tracer);
                ////    }
                ////}
                // m_customPlot->removeGraph(graph);
                removeList.append(curvePair.first);
            }
        }
        slot_removeSelectedTracer(tracerList);

        for (auto i : removeList)
        {
            auto _infoID = getCurveInfoByItem(i)->getInformationID();
            WindowTotalDataInstance->removeInfomationCurve2DBasicalByID(_infoID);
            removeCurveItemInfoPair(i);
        }
        m_customPlot->replot();

        connect(m_customPlot, &Curve2DPlotor::signal_updateSelectedGraph, this, &HeatMapWindow::slot_updateSelectedGraph);
    }

    void HeatMapWindow::updateCurve(QStandardItem* item, Info2DPointer& info)
    {
        auto _graph = info->getGraph();
        auto _reader = info->getReader();
        QCPColorMap* curveAdd = qobject_cast<QCPColorMap*>(info->getCurveLineAddress());
        auto valueIndex = info->getValueIndex();
        if (curveAdd == nullptr || _graph == nullptr || _reader == nullptr)
        {
            // TODO 警告
            return;
        }
        _reader->setScanParameterIndex(info->getScanParameterIndex());
        _reader->setSecondScanParameterIndex(info->getSecondScanParameterIndex());

        auto otherKeyData = info->getOtherKeyData();
        QVector<QVector<double>> others;
        for (auto i : otherKeyData)
        {
            others.append(QVector<double>{i});
        }
        _reader->setOtherKeyParameter(others);

        QList<Info2DPointer> newInfos;
        _reader->getChosenLinesFromMultiKey(QList<QPair<int, int>>{{valueIndex, info->getValueComponent()}}, newInfos);
        if (newInfos.size() != 1)
        {
            // TODO 警告， 应该只产生一组数据
            return;
        }
        auto xData = newInfos[0]->getXData();
        auto yData = newInfos[0]->getYData();
        // qDebug() << "yData =" << yData;
        info->setXData(xData);
        info->setYData(yData);

        int index = _reader->getRealValueIndex(info->getValueIndex(), info->getValueComponent());
        auto table = _reader->getOriginalTable();
        if (index < 0 || index >= _reader->getEachColUnitOfValue().size())
        {
            index = 0;
        }
        auto array = table->GetColumn(index + _reader->getEachColNameOfKey().size());
        TextArrayMetaStruct metaStruct;
        TextArrayMetaHander::getArrayMeta(array, metaStruct);
        bool isPower = false;
        switch (metaStruct.resultParaType)
        {
        case 7:
        case 13:
        case 16:
        case 17:
        case 18:
            isPower = true;
            break;
        default:
            break;
        }

        // TODO 取对数和归一化 针对单条曲线
        normalizeAndLog(yData.second, info->getIsNormalized(), info->getIsDB(), isPower);
        // curveAdd->setData(xData.second, yData.second);
        QVector<double> column1, column2, column3;
        column1 = info->getReader()->getEachColDataOfKey().at(info->getReader()->getScanParameterIndex());
        column2 = info->getReader()->getEachColDataOfKey().at(info->getReader()->getSecondScanParameterIndex());
        column3 = yData.second;
        SetHeatMapData(column1, column2, column3, curveAdd);

        QCPColorScale* colorScale = qobject_cast<QCPColorScale*>(m_customPlot->plotLayout()->element(1, 1));
        if (colorScale != nullptr)
        {
            if (m_autoColorScale)
            {
                curveAdd->rescaleDataRange(true);
            }
        }
        slot_adaptiveRange();
        // m_customPlot->rescaleAxes();
        auto title = generateGraphNameAuto(info, info->getIsDB());
        m_customPlot->setWindowTitleName(title);

        if (!m_isCurveRenamed)
        {
            info->setCurveName(yData.first);
            curveAdd->setName(yData.first);
            QSignalBlocker blocker(m_settings->getCurveNameModel()); // 开始阻断 signal
            item->setText(QString("电场_1[") + yData.first + "]");
            item->setToolTip(QString("电场_1[") + yData.first + "]");
        }
        m_customPlot->replot();
    }

    bool HeatMapWindow::eventFilter(QObject* watched, QEvent* event)
    {
        if (event->type() == QEvent::Wheel && watched == m_tracerPropertyWidget)
        {
            event->accept();
            return true;
        }
        return false;
    }

    void HeatMapWindow::SetHeatMapData(QVector<double> column1, QVector<double> column2, QVector<double> column3, QCPColorMap* colorMap)
    {
        if (colorMap == nullptr)
        {
            return;
        }

        QSet<double> set1, set2, set3;

        set1 = column1.toList().toSet();
        set2 = column2.toList().toSet();
        set3 = column3.toList().toSet();

        // 计算每一列的范围
        auto calculateRange = [](const QVector<double>& column)
        {
            if (column.isEmpty())
                return std::pair<double, double>(0.0, 0.0);

            double minVal = *std::min_element(column.begin(), column.end());
            double maxVal = *std::max_element(column.begin(), column.end());
            return std::make_pair(minVal, maxVal);
        };

        // 计算key是否为等差数列
        auto IsArithmeticProgression = [](const QVector<double>& column)
        {
            if (column.size() <= 1)
            {
                return false; // 空或单个元素不视为等差数列
            }
            double d = column.at(1) - column.at(0); // 初始公差
            for (int i = 2; i < column.size(); ++i)
            {
                double current_d = column.at(i) - column.at(i - 1);
                // 判断当前差与基准差的绝对误差是否在允许范围内
                if (std::abs(current_d - d) > std::numeric_limits<double>::epsilon())
                {
                    return false;
                }
            }
            if (std::abs(d - 0.0) > std::numeric_limits<double>::epsilon())
            {
                return false;
            }
            return true;
        };

        auto range1 = calculateRange(column1);
        auto range2 = calculateRange(column2);
        auto range3 = calculateRange(column3);

        int nx = set1.size();
        int ny = set2.size();
        int nz = column3.size();

        colorMap->setTightBoundary(true);
        colorMap->data()->setSize(nx, ny);                                                                        // we want the color map to have nx * ny data points
        colorMap->data()->setRange(QCPRange(range1.first, range1.second), QCPRange(range2.first, range2.second)); // and span the coordinate range -4..4 in both key (x) and value (y) dimensions
        // now we assign some data, by accessing the QCPColorMapData instance of the color map:
        double x, y, z;
        for (int xIndex = 0; xIndex < nx; ++xIndex)
        {
            for (int yIndex = 0; yIndex < ny; ++yIndex)
            {
                colorMap->data()->setCell(xIndex, yIndex, column3[xIndex * ny + yIndex]);
            }
        }
    }

    void HeatMapWindow::prepareAnimationData()
    {
        for (auto& infoPair : m_curveMap)
        {
            TextFileParser* _reader = infoPair.second->getReader();
            int valueIndex = infoPair.second->getValueIndex();
            int scanParameterIndex = infoPair.second->getScanParameterIndex();
            int secondScanParameterIndex = infoPair.second->getSecondScanParameterIndex();
            QVector<double> otherKeyData = infoPair.second->getOtherKeyData();
            QVector<QVector<double>> others;
            for (auto i : otherKeyData)
            {
                others.append(QVector<double>{i});
            }

            QVector<std::array<QVector<double>, 3>> animationDataList;
            int count = 0;
            auto readerList = _reader->GetOtherList();
            for (auto singleReader : readerList)
            {
                // qDebug() << " prepareAnimationData reader " + QString::number(count++) << "  " << singleReader;
                singleReader->setScanParameterIndex(scanParameterIndex);
                singleReader->setSecondScanParameterIndex(secondScanParameterIndex);
                singleReader->setOtherKeyParameter(others);

                // qDebug() << " prepareAnimationData reader " + QString::number(count++)
                //     << "scanParameterIndex " << scanParameterIndex << "secondScanParameterIndex "
                //     << secondScanParameterIndex << "others " << others;

                QList<Info2DPointer> newInfos;
                singleReader->getChosenLinesFromMultiKey(QList<QPair<int, int>>{{valueIndex, infoPair.second->getValueComponent()}}, newInfos);
                if (newInfos.size() != 1)
                {
                    // TODO 警告， 应该只产生一组数据
                    return;
                }
                auto xData = newInfos[0]->getXData();
                auto yData = newInfos[0]->getYData();

                int index = _reader->getRealValueIndex(infoPair.second->getValueIndex(), infoPair.second->getValueComponent());
                auto table = _reader->getOriginalTable();
                if (index < 0 || index >= _reader->getEachColUnitOfValue().size())
                {
                    index = 0;
                }
                auto arrayTmp = table->GetColumn(index + _reader->getEachColNameOfKey().size());
                TextArrayMetaStruct metaStruct;
                TextArrayMetaHander::getArrayMeta(arrayTmp, metaStruct);
                bool isPower = false;
                switch (metaStruct.resultParaType)
                {
                case 7:
                case 13:
                case 16:
                case 17:
                case 18:
                    isPower = true;
                    break;
                default:
                    break;
                }

                // TODO 取对数和归一化 针对单条曲线
                normalizeAndLog(yData.second, infoPair.second->getIsNormalized(), infoPair.second->getIsDB(), isPower);
                // curveAdd->setData(xData.second, yData.second);
                QVector<double> column1, column2, column3;
                column1 = infoPair.second->getReader()->getEachColDataOfKey().at(infoPair.second->getReader()->getScanParameterIndex());
                column2 = infoPair.second->getReader()->getEachColDataOfKey().at(infoPair.second->getReader()->getSecondScanParameterIndex());
                column3 = yData.second;
                std::array<QVector<double>, 3> array{column1, column2, column3};
                animationDataList.append(array);

                //QList<double> varValueList;
                //for (const auto& tmpPair : resultData->mulitFileInfo.second)
                //{
                //    varValueList.append(tmpPair.first);
                //}
                //info->setVarValueList(varValueList);

                // qDebug() << __func__ << "\n" << "column1 << " << column1 << "\n" << "column2 << " << column2 << "\n" << "column3 << " << column3;
            }
            m_imageSize < animationDataList.size() ? m_imageSize = animationDataList.size() : m_imageSize = m_imageSize;
            int currentAnimationIndex = readerList.indexOf(_reader);
            infoPair.second->setAnimationDataList(animationDataList);
            infoPair.second->setCurrentAnimationIndex(currentAnimationIndex);
        }
    }

    void HeatMapWindow::updateControlPanel()
    {
    }

    void HeatMapWindow::animationToNextDataFrame()
    {
        for (auto& infoPair : m_curveMap) // 每个动画数据
        {
            QCPColorMap* curveAdd = qobject_cast<QCPColorMap*>(infoPair.second->getCurveLineAddress());
            auto _reader = infoPair.second->getReader();
            auto animationList = infoPair.second->getAnimationDataList();
            auto valueIndex = infoPair.second->getValueIndex();
            auto readerList = _reader->GetOtherList();

            auto currentFrameID = infoPair.second->getCurrentAnimationIndex();

            if (m_isFirstTimeDataAnimation) // 第一次进行数据动图时，始终播放第一帧
            {
                currentFrameID = 0;
                m_isFirstTimeDataAnimation = false;
            }
            else
            {
                int tempID = currentFrameID + m_animationSettingData->sampleRate();
                currentFrameID = tempID >= animationList.size() ? 0 : tempID;
            }
            if (m_isBeginExportAnimation) // 是从导出动图启动，始终播放第一帧
            {
                currentFrameID = 0;
                m_isBeginExportAnimation = false;
                m_animationDataCount = 0;
            }

            _reader = readerList.at(currentFrameID);
            qDebug() << "currentFrameID: " << currentFrameID << "   reader = " << _reader;

            infoPair.second->setCurrentAnimationIndex(currentFrameID);
            auto currentData = animationList.at(currentFrameID);

            SetHeatMapData(currentData[0], currentData[1], currentData[2], curveAdd);
            // qDebug() << __func__ << "\n" << "currentData[0] << " << currentData[0] << "\n" << "currentData[1] << " << currentData[1] << "\n" << "currentData[2] << " << currentData[2];
            QList<Info2DPointer> newInfos;
            _reader->getChosenLinesFromMultiKey(QList<QPair<int, int>>{{valueIndex, infoPair.second->getValueComponent()}}, newInfos);
            if (newInfos.size() != 1)
            {
                // TODO 警告， 应该只产生一组数据
                return;
            }
            auto xData = newInfos[0]->getXData();
            auto yData = newInfos[0]->getYData();
            // qDebug() << "yData =" << yData;
            infoPair.second->setXData(xData);
            infoPair.second->setYData(yData);
            infoPair.second->setReader(_reader);

            curveAdd->setName(yData.first);

            // 更新dataSource
            auto item = getItemFromCurveInfo(infoPair.second);
            QString dataSource;
            slot_getSidebarCurveDataSource(item, dataSource);
            infoPair.second->setDataSources(dataSource);

            auto* model = qobject_cast<QStandardItemModel*>(m_settings->getCurveNameListView()->model());
            QItemSelectionModel* selectionModel = m_settings->getCurveNameListView()->selectionModel();
            QModelIndex idx = model->indexFromItem(item);
            bool isSelected = selectionModel->isSelected(idx);
            if (isSelected)
            {
                m_settings->setCurvePropertyPanelState(infoPair.second);
            }

            // item->setText(yData.first);
            sentToolBarAnimationDataInfo(infoPair.second);
            if (m_autoColorScale)
            {
                curveAdd->rescaleDataRange(true);
            }
        }
        slot_adaptiveRange();
        // m_customPlot->rescaleAxes();
        m_customPlot->replot();
    }

    void HeatMapWindow::exportAnimationToLocal()
    {
        if (m_animationImageList.empty())
        {
            emit IWidgetSignalInstance->signal_setToolButtonExportMotionGraph2InterfaceState(true);
            return;
        }
        int frameWidth = m_animationImageList[0].cols;
        int frameHeight = m_animationImageList[0].rows;
        cv::VideoWriter videoWriter;
        QFileInfo fileInfo(m_exportAnimationPath);
        auto suffix = fileInfo.suffix();
        auto frameRate = m_animationSettingData->frameRate();

        if (suffix == "gif")
        {
            CommonFunction::saveGifFromMatList(m_animationImageList, m_exportAnimationPath, frameRate);
        }
        else if (suffix == "avi")
        {
            videoWriter.open(m_exportAnimationPath.toStdString(), cv::VideoWriter::fourcc('X', 'V', 'I', 'D'),
                             frameRate, cv::Size(frameWidth, frameHeight), true);

            if (!videoWriter.isOpened())
            {
                qDebug() << "Could not open the output video file.";
                emit IBaseSignalInstance->signal_UpdateStatusBarInformation(QStringLiteral("导出动图失败！"), false);
                auto _res = IdaDialogMsg::showDialog(nullptr, QStringLiteral("导出动图失败！"), EDialogMsg::Error, QStringLiteral("失败"), {QStringLiteral("确定")});
                emit IWidgetSignalInstance->signal_setToolButtonExportMotionGraph2InterfaceState(true);
                return;
            }

            for (const auto& image : m_animationImageList)
            {
                videoWriter.write(image);
            }

            videoWriter.release();
            emit IBaseSignalInstance->signal_UpdateStatusBarInformation(QStringLiteral("导出动图完成！"), false);
            auto _res = IdaDialogMsg::showDialog(nullptr, QStringLiteral("导出动图成功！"), EDialogMsg::Normal, QStringLiteral("成功"), {QStringLiteral("确定")});
        }
        emit IWidgetSignalInstance->signal_setToolButtonExportMotionGraph2InterfaceState(true);
    }

    QString HeatMapWindow::generateGraphNameAuto(const Info2DPointer& info, bool isLog) const
    {
        auto reader = info->getReader();
        int index = reader->getRealValueIndex(info->getValueIndex(), info->getValueComponent());
        auto table = reader->getOriginalTable();
        if (index < 0 || index >= reader->getEachColUnitOfValue().size())
        {
            index = 0;
        }
        auto array = table->GetColumn(index + reader->getEachColNameOfKey().size());
        TextArrayMetaStruct metaStruct;
        TextArrayMetaHander::getArrayMeta(array, metaStruct);

        auto _title = getColResultTypeAsStringInner(metaStruct.resultParaType, false);
        auto _unit = metaStruct.unit;
        QString _logStr = isLog ? "dB" : "";
        QString postStr = "";
        if (!_logStr.isEmpty() || !_unit.isEmpty())
        {
            postStr = "[" + _logStr + _unit + "]";
        }
        QString title = _title + postStr;

        return title;
    }

    void HeatMapWindow::setAnimationTooBarInfoData(AnimationTooBarInfoData* info)
    {
        m_animationTooBarInfoData = info;
    }

    AnimationTooBarInfoData* HeatMapWindow::getAnimationTooBarInfoData()
    {
        return m_animationTooBarInfoData;
    }

    void HeatMapWindow::setFrameToBeforeExportAnimation()
    {
        for (auto& infoPair : m_curveMap) // 每个动画数据
        {
            QCPColorMap* curveAdd = qobject_cast<QCPColorMap*>(infoPair.second->getCurveLineAddress());
            auto _reader = infoPair.second->getReader();
            auto animationList = infoPair.second->getAnimationDataList();
            auto valueIndex = infoPair.second->getValueIndex();
            auto readerList = _reader->GetOtherList();

            auto currentFrameID = infoPair.second->getLastFrameIndexBeforeExportAnimation();

            infoPair.second->setCurrentAnimationIndex(currentFrameID);
            auto currentData = animationList.at(currentFrameID);

            SetHeatMapData(currentData[0], currentData[1], currentData[2], curveAdd);
            // qDebug() << __func__ << "\n" << "currentData[0] << " << currentData[0] << "\n" << "currentData[1] << " << currentData[1] << "\n" << "currentData[2] << " << currentData[2];
            QList<Info2DPointer> newInfos;
            _reader->getChosenLinesFromMultiKey(QList<QPair<int, int>>{{valueIndex, infoPair.second->getValueComponent()}}, newInfos);
            if (newInfos.size() != 1)
            {
                // TODO 警告， 应该只产生一组数据
                return;
            }
            auto xData = newInfos[0]->getXData();
            auto yData = newInfos[0]->getYData();
            // qDebug() << "yData =" << yData;
            infoPair.second->setXData(xData);
            infoPair.second->setYData(yData);
            infoPair.second->setReader(readerList.at(currentFrameID));

            curveAdd->setName(yData.first);
            sentToolBarAnimationDataInfo(infoPair.second);
        }

        //更新颜色条显示
        auto tmp = m_dataHeatGraph2DSettings;
        //tmp.copyData(dataGraph2DSettings);

        // colorScale
        auto colorScale = qobject_cast<QCPColorScale*>(m_customPlot->plotLayout()->element(1, 1));
        if (colorScale != nullptr)
        {
            this->SetColorScaleFlag(tmp->getColorBar()->m_isAutoRange);
            if (this->GetColorScaleFlag())
            {
                if (m_customPlot->plottableCount() > 0)
                {
                    auto colorMap = qobject_cast<QCPColorMap*>(m_customPlot->plottable(0));
                    colorMap->rescaleDataRange(true);
                }
            }
            else
            {
                auto rangeColorScale = colorScale->dataRange();
                rangeColorScale.upper = tmp->getColorBar()->m_maxValue;
                rangeColorScale.lower = tmp->getColorBar()->m_minValue;
                colorScale->setDataRange(rangeColorScale);
            }
            colorScale->axis()->setTickLabelColor(tmp->getColorBar()->m_fontColor);
            auto labelFonColorScale = colorScale->axis()->tickLabelFont();
            labelFonColorScale.setPointSize(tmp->getColorBar()->m_fontSize);
            colorScale->axis()->setTickLabelFont(labelFonColorScale);
            auto selectLabelFonColorScale = colorScale->axis()->selectedTickLabelFont();
            selectLabelFonColorScale.setPointSize(tmp->getColorBar()->m_fontSize);
            colorScale->axis()->setSelectedTickLabelFont(selectLabelFonColorScale);
        }

        slot_adaptiveRange();
        // m_customPlot->rescaleAxes();

        m_customPlot->replot();
    }
    void HeatMapWindow::sentToolBarAnimationDataInfo(const Info2DPointer& info)
    {
        auto currentFrameID = info->getCurrentAnimationIndex();

        // 右上角显示动图信息
        auto _type = info->getFieldType();
        auto unit = info->getUnit();
        auto varValueList = info->getVarValueList();

        QString typeString{};

        if (m_animationType == 0) // 数据变换
        {
            if (_type == 1)
            {
                typeString = "时间";
            }
            else if (_type == 2)
            {
                typeString = "频率"; // 三维曲面图数据变化始终是频率
            }
        }
        else // 相机
        {
            typeString = "角度";
            unit = "deg";
        }

        // TODO 拿到变量名称
        auto currentValue = varValueList[currentFrameID];
        QString firstLine = typeString + ": " + QString::number(currentValue) + " " + unit;

        auto sameRate = m_animationSettingData->sampleRate();
        QString secondLine = "采样率: " + QString::number(sameRate);

        auto frameRate = m_animationSettingData->frameRate();
        QString thirdLine = "播放速度: " + QString::number(frameRate);

        QStringList textActorList;
        textActorList << firstLine << secondLine << thirdLine;

        int totalNum = std::ceil(double(info->getAnimationDataList().size()) / sameRate);

        if (m_animationDataCount * sameRate >= info->getAnimationDataList().size())
        {
            m_animationDataCount = 0;
        }

        if (m_animationTooBarInfoData != nullptr)
        {
            m_animationTooBarInfoData->setType(typeString);
            m_animationTooBarInfoData->setUnit(unit);
            m_animationTooBarInfoData->setCurrentValue(QString::number(currentValue));
            m_animationTooBarInfoData->setSliderCurrentValue(currentFrameID);
            m_animationTooBarInfoData->setSliderMaxValue(totalNum);
        }

        emit IWidgetSignalInstance->signal_setToolBarAnimationDataInfo(typeString, unit, currentValue, totalNum, m_animationDataCount);
        ++m_animationDataCount;
    }

    void HeatMapWindow::slot_closeTree(const QList<QStandardItem*>& items)
    {
        QList<Info2DPointer> itemToDelete;
        for (auto treeItem : items)
        {
            for (auto infoPair : m_curveMap)
            {
                if (infoPair.second->getProjectTreeSourceItem() == treeItem)
                {
                    itemToDelete.append(infoPair.second);
                }
            }
        }
        deleteGraphs(itemToDelete);
    }

    void HeatMapWindow::deleteGraphs(const QList<Info2DPointer>& infos)
    {
        for (auto info : infos)
        {
            QCPColorMap* colorMap = qobject_cast<QCPColorMap*>(info->getCurveLineAddress());
            auto colorScale = colorMap->colorScale();
            colorScale->setVisible(false);
            m_customPlot->removePlottable(colorMap);

            auto item = getItemFromCurveInfo(info);

            auto model = qobject_cast<QStandardItemModel*>(m_settings->getCurveNameListView()->model());
            QModelIndex index = model->indexFromItem(item);
            model->removeRow(index.row());
            auto _infoID = info->getInformationID();
            WindowTotalDataInstance->removeInfomationCurve2DBasicalByID(_infoID);
            removeCurveItemInfoPair(item);
        }

        slot_adaptiveRange();
        m_customPlot->replot();
    }

    QHash<int, QString> HeatMapWindow::getMarkerNameMap()
    {
        QHash<int, QString> map;
        for (int i = 0; i < m_tracerPropertyWidget->rowCount(); ++i)
        {
            auto t = m_tracerPropertyWidget->item(i, 0)->data(QVariant::UserType).value<LineChartTracer*>();
            map.insert(i, t->GetName());
        }
        return map;
    }

    void HeatMapWindow::slot_exportMarkerData()
    {
        QString fileName = m_tracerPropertyWidget->exportDatas();

        QStringList _pyCode;
        _pyCode.append(QString("twodlinechart = PostProcessing.TwoDLineChart()"));
        _pyCode.append(QString("twodlinechart.setExportMarkerData(%1,'%2')").arg(m_windowId).arg(fileName));
        emit IBaseSignalInstance->signal_execPYCode(_pyCode, true, false);
    }

    void HeatMapWindow::pyExportMarkerData(QString fileName)
    {
        m_tracerPropertyWidget->setFileName(fileName);
        m_tracerPropertyWidget->exportDatas();
    }

} // namespace pst
